diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-05-02 17:23:31 +0200 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-05-02 17:23:31 +0200 |
commit | 06a68a09df243b175d1737a95df2e6f153cbae42 (patch) | |
tree | f29acf096ef401c6cab159cf10e448ba6e366b26 | |
parent | 7a93b9a11c99a88f293c3e1e3a79914b6d13b3aa (diff) | |
download | xen-06a68a09df243b175d1737a95df2e6f153cbae42.tar.gz xen-06a68a09df243b175d1737a95df2e6f153cbae42.tar.bz2 xen-06a68a09df243b175d1737a95df2e6f153cbae42.zip |
x86: make new_guest_cr3() preemptible
... as it may take significant 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: e2e6b7b627fec0d7a769ab46441f2985ebccbf04
master date: 2013-05-02 16:35:50 +0200
-rw-r--r-- | xen/arch/x86/mm.c | 76 | ||||
-rw-r--r-- | xen/arch/x86/traps.c | 9 |
2 files changed, 65 insertions, 20 deletions
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index f62e7c7c76..cc6242e668 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -2806,44 +2806,69 @@ int new_guest_cr3(unsigned long mfn) { struct vcpu *curr = current; struct domain *d = curr->domain; - int okay; + int rc; unsigned long old_base_mfn; #ifdef __x86_64__ if ( is_pv_32on64_domain(d) ) { - okay = paging_mode_refcounts(d) - ? 0 /* Old code was broken, but what should it be? */ - : mod_l4_entry( + rc = paging_mode_refcounts(d) + ? -EINVAL /* Old code was broken, but what should it be? */ + : mod_l4_entry( __va(pagetable_get_paddr(curr->arch.guest_table)), l4e_from_pfn( mfn, (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED)), - pagetable_get_pfn(curr->arch.guest_table), 0, 0, curr) == 0; - if ( unlikely(!okay) ) + pagetable_get_pfn(curr->arch.guest_table), 0, 1, curr); + switch ( rc ) { + case 0: + break; + case -EINTR: + case -EAGAIN: + return -EAGAIN; + default: MEM_LOG("Error while installing new compat baseptr %lx", mfn); - return 0; + return rc; } invalidate_shadow_ldt(curr, 0); write_ptbase(curr); - return 1; + return 0; } #endif - okay = paging_mode_refcounts(d) - ? get_page_from_pagenr(mfn, d) - : !get_page_and_type_from_pagenr(mfn, PGT_root_page_table, d, 0, 0); - if ( unlikely(!okay) ) + rc = put_old_guest_table(curr); + if ( unlikely(rc) ) + return rc; + + old_base_mfn = pagetable_get_pfn(curr->arch.guest_table); + /* + * This is particularly important when getting restarted after the + * previous attempt got preempted in the put-old-MFN phase. + */ + if ( old_base_mfn == mfn ) { - MEM_LOG("Error while installing new baseptr %lx", mfn); + write_ptbase(curr); return 0; } - invalidate_shadow_ldt(curr, 0); + rc = paging_mode_refcounts(d) + ? (get_page_from_pagenr(mfn, d) ? 0 : -EINVAL) + : get_page_and_type_from_pagenr(mfn, PGT_root_page_table, d, 0, 1); + switch ( rc ) + { + case 0: + break; + case -EINTR: + case -EAGAIN: + return -EAGAIN; + default: + MEM_LOG("Error while installing new baseptr %lx", mfn); + return rc; + } - old_base_mfn = pagetable_get_pfn(curr->arch.guest_table); + invalidate_shadow_ldt(curr, 0); curr->arch.guest_table = pagetable_from_pfn(mfn); update_cr3(curr); @@ -2852,13 +2877,25 @@ int new_guest_cr3(unsigned long mfn) if ( likely(old_base_mfn != 0) ) { + struct page_info *page = mfn_to_page(old_base_mfn); + if ( paging_mode_refcounts(d) ) - put_page(mfn_to_page(old_base_mfn)); + put_page(page); else - put_page_and_type(mfn_to_page(old_base_mfn)); + switch ( rc = put_page_and_type_preemptible(page, 1) ) + { + case -EINTR: + rc = -EAGAIN; + case -EAGAIN: + curr->arch.old_guest_table = page; + break; + default: + BUG_ON(rc); + break; + } } - return 1; + return rc; } static struct domain *get_pg_owner(domid_t domid) @@ -3154,7 +3191,8 @@ long do_mmuext_op( } case MMUEXT_NEW_BASEPTR: - okay = new_guest_cr3(gmfn_to_mfn(d, op.arg1.mfn)); + rc = new_guest_cr3(gmfn_to_mfn(d, op.arg1.mfn)); + okay = !rc; break; #ifdef __x86_64__ diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 5a48a2aeac..7e4e6b69ea 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -2317,8 +2317,15 @@ static int emulate_privileged_op(struct cpu_user_regs *regs) rc = new_guest_cr3(gmfn_to_mfn(v->domain, compat_cr3_to_pfn(*reg))); #endif domain_unlock(v->domain); - if ( rc == 0 ) /* not okay */ + switch ( rc ) + { + case 0: + break; + case -EAGAIN: /* retry after preemption */ + goto skip; + default: /* not okay */ goto fail; + } break; case 4: /* Write CR4 */ |