$NetBSD: patch-CVE-2013-1918_11,v 1.1 2013/05/03 16:48:37 drochner Exp $

--- xen/arch/x86/domain.c.orig	2013-05-03 13:27:23.000000000 +0000
+++ xen/arch/x86/domain.c
@@ -70,8 +70,6 @@ void (*dead_idle) (void) __read_mostly =
 static void paravirt_ctxt_switch_from(struct vcpu *v);
 static void paravirt_ctxt_switch_to(struct vcpu *v);
 
-static void vcpu_destroy_pagetables(struct vcpu *v);
-
 static void continue_idle_domain(struct vcpu *v)
 {
     reset_stack_and_jump(idle_loop);
@@ -678,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;
 
@@ -817,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);
@@ -904,17 +934,16 @@ int arch_set_info_guest(
 #undef c
 }
 
-void arch_vcpu_reset(struct vcpu *v)
+int arch_vcpu_reset(struct vcpu *v)
 {
     if ( !is_hvm_vcpu(v) )
     {
         destroy_gdt(v);
-        vcpu_destroy_pagetables(v);
-    }
-    else
-    {
-        vcpu_end_shutdown_deferral(v);
+        return vcpu_destroy_pagetables(v);
     }
+
+    vcpu_end_shutdown_deferral(v);
+    return 0;
 }
 
 /* 
@@ -1917,63 +1946,6 @@ static int relinquish_memory(
     return ret;
 }
 
-static void vcpu_destroy_pagetables(struct vcpu *v)
-{
-    struct domain *d = v->domain;
-    unsigned long pfn;
-
-#ifdef __x86_64__
-    if ( is_pv_32on64_vcpu(v) )
-    {
-        pfn = l4e_get_pfn(*(l4_pgentry_t *)
-                          __va(pagetable_get_paddr(v->arch.guest_table)));
-
-        if ( pfn != 0 )
-        {
-            if ( paging_mode_refcounts(d) )
-                put_page(mfn_to_page(pfn));
-            else
-                put_page_and_type(mfn_to_page(pfn));
-        }
-
-        l4e_write(
-            (l4_pgentry_t *)__va(pagetable_get_paddr(v->arch.guest_table)),
-            l4e_empty());
-
-        v->arch.cr3 = 0;
-        return;
-    }
-#endif
-
-    pfn = pagetable_get_pfn(v->arch.guest_table);
-    if ( pfn != 0 )
-    {
-        if ( paging_mode_refcounts(d) )
-            put_page(mfn_to_page(pfn));
-        else
-            put_page_and_type(mfn_to_page(pfn));
-        v->arch.guest_table = pagetable_null();
-    }
-
-#ifdef __x86_64__
-    /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */
-    pfn = pagetable_get_pfn(v->arch.guest_table_user);
-    if ( pfn != 0 )
-    {
-        if ( !is_pv_32bit_vcpu(v) )
-        {
-            if ( paging_mode_refcounts(d) )
-                put_page(mfn_to_page(pfn));
-            else
-                put_page_and_type(mfn_to_page(pfn));
-        }
-        v->arch.guest_table_user = pagetable_null();
-    }
-#endif
-
-    v->arch.cr3 = 0;
-}
-
 int domain_relinquish_resources(struct domain *d)
 {
     int ret;
@@ -1992,7 +1964,9 @@ int domain_relinquish_resources(struct d
         for_each_vcpu ( d, v )
         {
             /* Drop the in-use references to page-table bases. */
-            vcpu_destroy_pagetables(v);
+            ret = vcpu_destroy_pagetables(v);
+            if ( ret )
+                return ret;
 
             /*
              * Relinquish GDT mappings. No need for explicit unmapping of the
