]> xenbits.xensource.com Git - people/aperard/xen-arm.git/commitdiff
x86: make new_guest_cr3() preemptible
authorJan Beulich <jbeulich@suse.com>
Thu, 2 May 2013 14:35:50 +0000 (16:35 +0200)
committerJan Beulich <jbeulich@suse.com>
Thu, 2 May 2013 14:35:50 +0000 (16:35 +0200)
... 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>
xen/arch/x86/mm.c
xen/arch/x86/traps.c

index 9a076e9d36dd9fda1d59e58426c8896cc530ad4d..e43a698791e2809ec47826a9f08e42c5b91a3c32 100644 (file)
@@ -2656,7 +2656,7 @@ 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;
 
     if ( is_pv_32on64_domain(d) )
@@ -2664,39 +2664,64 @@ int new_guest_cr3(unsigned long mfn)
         unsigned long gt_mfn = pagetable_get_pfn(curr->arch.guest_table);
         l4_pgentry_t *pl4e = map_domain_page(gt_mfn);
 
-        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(
                     pl4e,
                     l4e_from_pfn(
                         mfn,
                         (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED)),
-                    gt_mfn, 0, 0, curr) == 0;
+                    gt_mfn, 0, 1, curr);
         unmap_domain_page(pl4e);
-        if ( unlikely(!okay) )
+        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;
     }
 
-    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);
@@ -2705,13 +2730,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)
@@ -2982,8 +3019,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;
 
         case MMUEXT_NEW_USER_BASEPTR: {
index d36eddd229209b46a48366d9c9fac8df0884732a..4de9313c7467dc987eccdabb8c3e32a8927029e0 100644 (file)
@@ -2322,12 +2322,23 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
             gfn = !is_pv_32on64_vcpu(v)
                 ? xen_cr3_to_pfn(*reg) : compat_cr3_to_pfn(*reg);
             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;
         }