aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-05-02 17:25:37 +0200
committerJan Beulich <jbeulich@suse.com>2013-05-02 17:25:37 +0200
commit02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0 (patch)
tree25274eb2ba9c70bfefe29d64c764101555517ca2
parentc6fad967aabeb98da8307e59f73474b23b42f0d1 (diff)
downloadxen-02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0.tar.gz
xen-02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0.tar.bz2
xen-02615aaefd5c6c5856b4f81799bb6ec1ca0d89d0.zip
x86: make arch_set_info_guest() preemptible
.. 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> master commit: 99d2b149915010e986f4d8778708c5891e7b4635 master date: 2013-05-02 16:38:30 +0200
-rw-r--r--xen/arch/x86/domain.c108
-rw-r--r--xen/common/compat/domain.c4
-rw-r--r--xen/common/domain.c5
-rw-r--r--xen/common/domctl.c4
4 files changed, 83 insertions, 38 deletions
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 696eff3b3b..9a3448867d 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -676,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;
@@ -815,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);
diff --git a/xen/common/compat/domain.c b/xen/common/compat/domain.c
index 67e0e5e316..5fe393f16a 100644
--- a/xen/common/compat/domain.c
+++ b/xen/common/compat/domain.c
@@ -52,6 +52,10 @@ int compat_vcpu_op(int cmd, int vcpuid, XEN_GUEST_HANDLE(void) arg)
rc = boot_vcpu(d, vcpuid, cmp_ctxt);
domain_unlock(d);
+ if ( rc == -EAGAIN )
+ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih",
+ cmd, vcpuid, arg);
+
xfree(cmp_ctxt);
break;
}
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 4f2efe504e..5fa045bf71 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -842,6 +842,11 @@ long do_vcpu_op(int cmd, int vcpuid, XEN_GUEST_HANDLE(void) arg)
domain_unlock(d);
xfree(ctxt);
+
+ if ( rc == -EAGAIN )
+ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih",
+ cmd, vcpuid, arg);
+
break;
case VCPUOP_up:
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index 65e30df1d3..c3240db03c 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -318,6 +318,10 @@ long do_domctl(XEN_GUEST_HANDLE(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);
}
svc_out: