6f6e96c
x86: make vcpu_reset() preemptible
6f6e96c
6f6e96c
... as dropping the old page tables may take significant amounts of
6f6e96c
time.
6f6e96c
6f6e96c
This is part of CVE-2013-1918 / XSA-45.
6f6e96c
6f6e96c
Signed-off-by: Jan Beulich <jbeulich@suse.com>
6f6e96c
Acked-by: Tim Deegan <tim@xen.org>
6f6e96c
6f6e96c
--- a/xen/arch/x86/domain.c
6f6e96c
+++ b/xen/arch/x86/domain.c
6f6e96c
@@ -902,17 +902,16 @@ int arch_set_info_guest(
6f6e96c
 #undef c
6f6e96c
 }
6f6e96c
 
6f6e96c
-void arch_vcpu_reset(struct vcpu *v)
6f6e96c
+int arch_vcpu_reset(struct vcpu *v)
6f6e96c
 {
6f6e96c
     if ( !is_hvm_vcpu(v) )
6f6e96c
     {
6f6e96c
         destroy_gdt(v);
6f6e96c
-        vcpu_destroy_pagetables(v, 0);
6f6e96c
-    }
6f6e96c
-    else
6f6e96c
-    {
6f6e96c
-        vcpu_end_shutdown_deferral(v);
6f6e96c
+        return vcpu_destroy_pagetables(v);
6f6e96c
     }
6f6e96c
+
6f6e96c
+    vcpu_end_shutdown_deferral(v);
6f6e96c
+    return 0;
6f6e96c
 }
6f6e96c
 
6f6e96c
 /* 
6f6e96c
@@ -1933,7 +1932,7 @@ int domain_relinquish_resources(struct d
6f6e96c
         for_each_vcpu ( d, v )
6f6e96c
         {
6f6e96c
             /* Drop the in-use references to page-table bases. */
6f6e96c
-            ret = vcpu_destroy_pagetables(v, 1);
6f6e96c
+            ret = vcpu_destroy_pagetables(v);
6f6e96c
             if ( ret )
6f6e96c
                 return ret;
6f6e96c
 
6f6e96c
--- a/xen/arch/x86/hvm/hvm.c
6f6e96c
+++ b/xen/arch/x86/hvm/hvm.c
6f6e96c
@@ -3083,8 +3083,11 @@ static void hvm_s3_suspend(struct domain
6f6e96c
 
6f6e96c
     for_each_vcpu ( d, v )
6f6e96c
     {
6f6e96c
+        int rc;
6f6e96c
+
6f6e96c
         vlapic_reset(vcpu_vlapic(v));
6f6e96c
-        vcpu_reset(v);
6f6e96c
+        rc = vcpu_reset(v);
6f6e96c
+        ASSERT(!rc);
6f6e96c
     }
6f6e96c
 
6f6e96c
     vpic_reset(d);
6f6e96c
--- a/xen/arch/x86/hvm/vlapic.c
6f6e96c
+++ b/xen/arch/x86/hvm/vlapic.c
6f6e96c
@@ -252,10 +252,13 @@ static void vlapic_init_sipi_action(unsi
6f6e96c
     {
6f6e96c
     case APIC_DM_INIT: {
6f6e96c
         bool_t fpu_initialised;
6f6e96c
+        int rc;
6f6e96c
+
6f6e96c
         domain_lock(target->domain);
6f6e96c
         /* Reset necessary VCPU state. This does not include FPU state. */
6f6e96c
         fpu_initialised = target->fpu_initialised;
6f6e96c
-        vcpu_reset(target);
6f6e96c
+        rc = vcpu_reset(target);
6f6e96c
+        ASSERT(!rc);
6f6e96c
         target->fpu_initialised = fpu_initialised;
6f6e96c
         vlapic_reset(vcpu_vlapic(target));
6f6e96c
         domain_unlock(target->domain);
6f6e96c
--- a/xen/arch/x86/mm.c
6f6e96c
+++ b/xen/arch/x86/mm.c
6f6e96c
@@ -2744,7 +2744,7 @@ static int put_old_guest_table(struct vc
6f6e96c
     return rc;
6f6e96c
 }
6f6e96c
 
6f6e96c
-int vcpu_destroy_pagetables(struct vcpu *v, bool_t preemptible)
6f6e96c
+int vcpu_destroy_pagetables(struct vcpu *v)
6f6e96c
 {
6f6e96c
     unsigned long mfn = pagetable_get_pfn(v->arch.guest_table);
6f6e96c
     struct page_info *page;
6f6e96c
@@ -2764,7 +2764,7 @@ int vcpu_destroy_pagetables(struct vcpu 
6f6e96c
         if ( paging_mode_refcounts(v->domain) )
6f6e96c
             put_page(page);
6f6e96c
         else
6f6e96c
-            rc = put_page_and_type_preemptible(page, preemptible);
6f6e96c
+            rc = put_page_and_type_preemptible(page, 1);
6f6e96c
     }
6f6e96c
 
6f6e96c
 #ifdef __x86_64__
6f6e96c
@@ -2790,7 +2790,7 @@ int vcpu_destroy_pagetables(struct vcpu 
6f6e96c
             if ( paging_mode_refcounts(v->domain) )
6f6e96c
                 put_page(page);
6f6e96c
             else
6f6e96c
-                rc = put_page_and_type_preemptible(page, preemptible);
6f6e96c
+                rc = put_page_and_type_preemptible(page, 1);
6f6e96c
         }
6f6e96c
         if ( !rc )
6f6e96c
             v->arch.guest_table_user = pagetable_null();
6f6e96c
--- a/xen/common/domain.c
6f6e96c
+++ b/xen/common/domain.c
6f6e96c
@@ -770,14 +770,18 @@ int boot_vcpu(struct domain *d, int vcpu
6f6e96c
     return arch_set_info_guest(v, ctxt);
6f6e96c
 }
6f6e96c
 
6f6e96c
-void vcpu_reset(struct vcpu *v)
6f6e96c
+int vcpu_reset(struct vcpu *v)
6f6e96c
 {
6f6e96c
     struct domain *d = v->domain;
6f6e96c
+    int rc;
6f6e96c
 
6f6e96c
     vcpu_pause(v);
6f6e96c
     domain_lock(d);
6f6e96c
 
6f6e96c
-    arch_vcpu_reset(v);
6f6e96c
+    set_bit(_VPF_in_reset, &v->pause_flags);
6f6e96c
+    rc = arch_vcpu_reset(v);
6f6e96c
+    if ( rc )
6f6e96c
+        goto out_unlock;
6f6e96c
 
6f6e96c
     set_bit(_VPF_down, &v->pause_flags);
6f6e96c
 
6f6e96c
@@ -793,9 +797,13 @@ void vcpu_reset(struct vcpu *v)
6f6e96c
 #endif
6f6e96c
     cpus_clear(v->cpu_affinity_tmp);
6f6e96c
     clear_bit(_VPF_blocked, &v->pause_flags);
6f6e96c
+    clear_bit(_VPF_in_reset, &v->pause_flags);
6f6e96c
 
6f6e96c
+ out_unlock:
6f6e96c
     domain_unlock(v->domain);
6f6e96c
     vcpu_unpause(v);
6f6e96c
+
6f6e96c
+    return rc;
6f6e96c
 }
6f6e96c
 
6f6e96c
 
6f6e96c
--- a/xen/common/domctl.c
6f6e96c
+++ b/xen/common/domctl.c
6f6e96c
@@ -286,8 +286,10 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domc
6f6e96c
 
6f6e96c
         if ( guest_handle_is_null(op->u.vcpucontext.ctxt) )
6f6e96c
         {
6f6e96c
-            vcpu_reset(v);
6f6e96c
-            ret = 0;
6f6e96c
+            ret = vcpu_reset(v);
6f6e96c
+            if ( ret == -EAGAIN )
6f6e96c
+                ret = hypercall_create_continuation(
6f6e96c
+                          __HYPERVISOR_domctl, "h", u_domctl);
6f6e96c
             goto svc_out;
6f6e96c
         }
6f6e96c
 
6f6e96c
--- a/xen/include/asm-x86/mm.h
6f6e96c
+++ b/xen/include/asm-x86/mm.h
6f6e96c
@@ -555,7 +555,7 @@ void audit_domains(void);
6f6e96c
 int new_guest_cr3(unsigned long pfn);
6f6e96c
 void make_cr3(struct vcpu *v, unsigned long mfn);
6f6e96c
 void update_cr3(struct vcpu *v);
6f6e96c
-int vcpu_destroy_pagetables(struct vcpu *, bool_t preemptible);
6f6e96c
+int vcpu_destroy_pagetables(struct vcpu *);
6f6e96c
 void propagate_page_fault(unsigned long addr, u16 error_code);
6f6e96c
 void *do_page_walk(struct vcpu *v, unsigned long addr);
6f6e96c
 
6f6e96c
--- a/xen/include/xen/domain.h
6f6e96c
+++ b/xen/include/xen/domain.h
6f6e96c
@@ -15,7 +15,7 @@ struct vcpu *alloc_vcpu(
6f6e96c
 int boot_vcpu(
6f6e96c
     struct domain *d, int vcpuid, vcpu_guest_context_u ctxt);
6f6e96c
 struct vcpu *alloc_dom0_vcpu0(void);
6f6e96c
-void vcpu_reset(struct vcpu *v);
6f6e96c
+int vcpu_reset(struct vcpu *);
6f6e96c
 
6f6e96c
 struct xen_domctl_getdomaininfo;
6f6e96c
 void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info);
6f6e96c
@@ -57,7 +57,7 @@ void arch_dump_vcpu_info(struct vcpu *v)
6f6e96c
 
6f6e96c
 void arch_dump_domain_info(struct domain *d);
6f6e96c
 
6f6e96c
-void arch_vcpu_reset(struct vcpu *v);
6f6e96c
+int arch_vcpu_reset(struct vcpu *);
6f6e96c
 
6f6e96c
 bool_t domctl_lock_acquire(void);
6f6e96c
 void domctl_lock_release(void);
6f6e96c
--- a/xen/include/xen/sched.h
6f6e96c
+++ b/xen/include/xen/sched.h
6f6e96c
@@ -597,6 +597,9 @@ extern struct domain *domain_list;
6f6e96c
  /* VCPU is blocked on memory-event ring. */
6f6e96c
 #define _VPF_mem_event       4
6f6e96c
 #define VPF_mem_event        (1UL<<_VPF_mem_event)
6f6e96c
+ /* VCPU is being reset. */
6f6e96c
+#define _VPF_in_reset        7
6f6e96c
+#define VPF_in_reset         (1UL<<_VPF_in_reset)
6f6e96c
 
6f6e96c
 static inline int vcpu_runnable(struct vcpu *v)
6f6e96c
 {