]> xenbits.xensource.com Git - people/iwj/xen.git/commitdiff
x86: make arch_set_info_guest() preemptible
authorJan Beulich <jbeulich@suse.com>
Thu, 2 May 2013 14:38:30 +0000 (16:38 +0200)
committerJan Beulich <jbeulich@suse.com>
Thu, 2 May 2013 14:38:30 +0000 (16:38 +0200)
.. 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>
xen/arch/x86/domain.c
xen/common/compat/domain.c
xen/common/domain.c
xen/common/domctl.c

index 5bf52ff5663216ee5c1bc2f6ab3b7bb11a906865..0baaa951af098a9c8f1a1f4b8acc6c2386674108 100644 (file)
@@ -752,6 +752,9 @@ int arch_set_info_guest(
 
     if ( !v->is_initialised )
     {
+        if ( !compat && !(flags & VGCF_in_kernel) && !c.nat->ctrlreg[1] )
+            return -EINVAL;
+
         v->arch.pv_vcpu.ldt_base = c(ldt_base);
         v->arch.pv_vcpu.ldt_ents = c(ldt_ents);
     }
@@ -844,80 +847,86 @@ int arch_set_info_guest(
     if ( rc != 0 )
         return rc;
 
+    set_bit(_VPF_in_reset, &v->pause_flags);
+
     if ( !compat )
-    {
         cr3_gfn = xen_cr3_to_pfn(c.nat->ctrlreg[3]);
-        cr3_page = get_page_from_gfn(d, cr3_gfn, NULL, P2M_ALLOC);
-
-        if ( !cr3_page )
-        {
-            destroy_gdt(v);
-            return -EINVAL;
-        }
-        if ( !paging_mode_refcounts(d)
-             && !get_page_type(cr3_page, PGT_base_page_table) )
-        {
-            put_page(cr3_page);
-            destroy_gdt(v);
-            return -EINVAL;
-        }
+    else
+        cr3_gfn = compat_cr3_to_pfn(c.cmp->ctrlreg[3]);
+    cr3_page = get_page_from_gfn(d, cr3_gfn, NULL, P2M_ALLOC);
 
+    if ( !cr3_page )
+        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_page(cr3_page);
         if ( c.nat->ctrlreg[1] )
         {
             cr3_gfn = xen_cr3_to_pfn(c.nat->ctrlreg[1]);
             cr3_page = get_page_from_gfn(d, cr3_gfn, NULL, P2M_ALLOC);
 
-            if ( !cr3_page ||
-                 (!paging_mode_refcounts(d)
-                  && !get_page_type(cr3_page, PGT_base_page_table)) )
+            if ( !cr3_page )
+                rc = -EINVAL;
+            else if ( !paging_mode_refcounts(d) )
             {
-                if (cr3_page)
-                    put_page(cr3_page);
-                cr3_page = pagetable_get_page(v->arch.guest_table);
-                v->arch.guest_table = pagetable_null();
-                if ( paging_mode_refcounts(d) )
-                    put_page(cr3_page);
-                else
-                    put_page_and_type(cr3_page);
-                destroy_gdt(v);
-                return -EINVAL;
+                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_page(cr3_page);
-        }
-        else if ( !(flags & VGCF_in_kernel) )
-        {
-            destroy_gdt(v);
-            return -EINVAL;
+            if ( !rc )
+               v->arch.guest_table_user = pagetable_from_page(cr3_page);
         }
     }
     else
     {
         l4_pgentry_t *l4tab;
 
-        cr3_gfn = compat_cr3_to_pfn(c.cmp->ctrlreg[3]);
-        cr3_page = get_page_from_gfn(d, cr3_gfn, NULL, P2M_ALLOC);
-
-        if ( !cr3_page)
-        {
-            destroy_gdt(v);
-            return -EINVAL;
-        }
-
-        if (!paging_mode_refcounts(d)
-            && !get_page_type(cr3_page, PGT_l3_page_table) )
-        {
-            put_page(cr3_page);
-            destroy_gdt(v);
-            return -EINVAL;
-        }
-
         l4tab = map_domain_page(pagetable_get_pfn(v->arch.guest_table));
         *l4tab = l4e_from_pfn(page_to_mfn(cr3_page),
             _PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED);
         unmap_domain_page(l4tab);
     }
+    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);
index aac8f46eb5273be9331bd20e657163fa9938ecce..44ba78ddc78fc083029c2aafedf9db12a65b2f71 100644 (file)
@@ -50,6 +50,10 @@ int compat_vcpu_op(int cmd, int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg)
         rc = v->is_initialised ? -EEXIST : arch_set_info_guest(v, cmp_ctxt);
         domain_unlock(d);
 
+        if ( rc == -EAGAIN )
+            rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih",
+                                               cmd, vcpuid, arg);
+
         xfree(cmp_ctxt);
         break;
     }
index 7cca65507d8243d046544b690d5707816d0f0c16..b5d44d466b32b8ad190979e8abba58d19a30d0ab 100644 (file)
@@ -938,6 +938,11 @@ long do_vcpu_op(int cmd, int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg)
         domain_unlock(d);
 
         free_vcpu_guest_context(ctxt);
+
+        if ( rc == -EAGAIN )
+            rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih",
+                                               cmd, vcpuid, arg);
+
         break;
 
     case VCPUOP_up: {
index 1d00cfc95f08f762d7a96fba0d9e2d1f90243fd4..9bd8f8068d5d321f06e433a0669d6c5fe6e1a50d 100644 (file)
@@ -368,6 +368,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(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);
         }
 
         free_vcpu_guest_context(c.nat);