aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/mm.c
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-05-02 16:34:21 +0200
committerJan Beulich <jbeulich@suse.com>2013-05-02 16:34:21 +0200
commit6cdc9be2a5f2a87b4504404fbf648d16d9503c19 (patch)
tree57244e0df782cc38467cf1045314806967c72ba9 /xen/arch/x86/mm.c
parent9626d1c1fafe2da5af6e59478c9e9db6d03144df (diff)
downloadxen-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.c86
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) )
{