174 lines
5.1 KiB
Diff
174 lines
5.1 KiB
Diff
|
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>
|
||
|
|
||
|
Index: xen-4.2.1-testing/xen/arch/x86/mm.c
|
||
|
===================================================================
|
||
|
--- xen-4.2.1-testing.orig/xen/arch/x86/mm.c
|
||
|
+++ xen-4.2.1-testing/xen/arch/x86/mm.c
|
||
|
@@ -2906,44 +2906,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);
|
||
|
@@ -2952,13 +2977,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)
|
||
|
@@ -3256,8 +3293,13 @@ long do_mmuext_op(
|
||
|
}
|
||
|
|
||
|
case MMUEXT_NEW_BASEPTR:
|
||
|
- okay = (!paging_mode_translate(d)
|
||
|
- && new_guest_cr3(op.arg1.mfn));
|
||
|
+ if ( paging_mode_translate(d) )
|
||
|
+ okay = 0;
|
||
|
+ else
|
||
|
+ {
|
||
|
+ rc = new_guest_cr3(op.arg1.mfn);
|
||
|
+ okay = !rc;
|
||
|
+ }
|
||
|
break;
|
||
|
|
||
|
|
||
|
Index: xen-4.2.1-testing/xen/arch/x86/traps.c
|
||
|
===================================================================
|
||
|
--- xen-4.2.1-testing.orig/xen/arch/x86/traps.c
|
||
|
+++ xen-4.2.1-testing/xen/arch/x86/traps.c
|
||
|
@@ -2407,12 +2407,23 @@ static int emulate_privileged_op(struct
|
||
|
#endif
|
||
|
}
|
||
|
page = get_page_from_gfn(v->domain, gfn, NULL, P2M_ALLOC);
|
||
|
- rc = page ? new_guest_cr3(page_to_mfn(page)) : 0;
|
||
|
if ( page )
|
||
|
+ {
|
||
|
+ rc = new_guest_cr3(page_to_mfn(page));
|
||
|
put_page(page);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ rc = -EINVAL;
|
||
|
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;
|
||
|
}
|
||
|
|