x86: make vcpu_destroy_pagetables() preemptible

... as it may take significant amounts of time.

The function, being moved to mm.c as the better home for it anyway, and
to avoid having to make a new helper function there non-static, is
given a "preemptible" parameter temporarily (until, in a subsequent
patch, its other caller is also being made capable of dealing with
preemption).

This is part of CVE-2013-1918 / XSA-45.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Tim Deegan <tim@xen.org>
This commit is contained in:
Jan Beulich committed 2013-05-02 16:34:21 +02:00
1 parent 9626d1c1fa
commit 6cdc9be2a5
5 files changed
+91 -59

No files matched your search

+4 -56
View File
@@ -71,8 +71,6 @@ void (*dead_idle) (void) __read_mostly = default_dead_idle;
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 default_idle(void)
{
local_irq_disable();
@@ -946,7 +944,7 @@ void arch_vcpu_reset(struct vcpu *v)
if ( !is_hvm_vcpu(v) )
{
destroy_gdt(v);
vcpu_destroy_pagetables(v);
vcpu_destroy_pagetables(v, 0);
}
else
{
@@ -1944,58 +1942,6 @@ static int relinquish_memory(
return ret;
}
static void vcpu_destroy_pagetables(struct vcpu *v)
{
struct domain *d = v->domain;
unsigned long pfn = pagetable_get_pfn(v->arch.guest_table);
if ( is_pv_32on64_vcpu(v) )
{
l4_pgentry_t *l4tab = map_domain_page(pfn);
pfn = l4e_get_pfn(*l4tab);
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(l4tab, l4e_empty());
unmap_domain_page(l4tab);
v->arch.cr3 = 0;
return;
}
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();
}
/* 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();
}
v->arch.cr3 = 0;
}
int domain_relinquish_resources(struct domain *d)
{
int ret;
@@ -2014,7 +1960,9 @@ int domain_relinquish_resources(struct domain *d)
/* Drop the in-use references to page-table bases. */
for_each_vcpu ( d, v )
{
vcpu_destroy_pagetables(v);
ret = vcpu_destroy_pagetables(v, 1);
if ( ret )
return ret;
unmap_vcpu_info(v);
}
+84 -2
View File
@@ -2579,6 +2579,79 @@ static void put_superpage(unsigned long mfn)
return;
}
static int put_old_guest_table(struct vcpu *v)
{
int rc;
if ( !v->arch.old_guest_table )
return 0;
switch ( rc = put_page_and_type_preemptible(v->arch.old_guest_table, 1) )
{
case -EINTR:
case -EAGAIN:
return -EAGAIN;
}
v->arch.old_guest_table = NULL;
return rc;
}
int vcpu_destroy_pagetables(struct vcpu *v, bool_t preemptible)
{
unsigned long mfn = pagetable_get_pfn(v->arch.guest_table);
struct page_info *page;
l4_pgentry_t *l4tab = NULL;
int rc = put_old_guest_table(v);
if ( rc )
return rc;
if ( is_pv_32on64_vcpu(v) )
{
l4tab = map_domain_page(mfn);
mfn = l4e_get_pfn(*l4tab);
}
if ( mfn )
{
page = mfn_to_page(mfn);
if ( paging_mode_refcounts(v->domain) )
put_page(page);
else
rc = put_page_and_type_preemptible(page, preemptible);
}
if ( l4tab )
{
if ( !rc )
l4e_write(l4tab, l4e_empty());
unmap_domain_page(l4tab);
}
else if ( !rc )
{
v->arch.guest_table = pagetable_null();
/* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */
mfn = pagetable_get_pfn(v->arch.guest_table_user);
if ( mfn )
{
page = mfn_to_page(mfn);
if ( paging_mode_refcounts(v->domain) )
put_page(page);
else
rc = put_page_and_type_preemptible(page, preemptible);
}
if ( !rc )
v->arch.guest_table_user = pagetable_null();
}
v->arch.cr3 = 0;
return rc;
}
int new_guest_cr3(unsigned long mfn)
{
struct vcpu *curr = current;
@@ -2733,12 +2806,21 @@ long do_mmuext_op(
unsigned int foreigndom)
{
struct mmuext_op op;
int rc = 0, i = 0, okay;
unsigned long type;
unsigned int done = 0;
unsigned int i = 0, done = 0;
struct vcpu *curr = current;
struct domain *d = curr->domain;
struct domain *pg_owner;
int okay, rc = put_old_guest_table(curr);
if ( unlikely(rc) )
{
if ( likely(rc == -EAGAIN) )
rc = hypercall_create_continuation(
__HYPERVISOR_mmuext_op, "hihi", uops, count, pdone,
foreigndom);
return rc;
}
if ( unlikely(count & MMU_UPDATE_PREEMPTED) )
{
+1 -1
View File
@@ -365,7 +365,7 @@ int compat_mmuext_op(XEN_GUEST_HANDLE_PARAM(mmuext_op_compat_t) cmp_uops,
: mcs->call.args[1];
unsigned int left = arg1 & ~MMU_UPDATE_PREEMPTED;
BUG_ON(left == arg1);
BUG_ON(left == arg1 && left != i);
BUG_ON(left > count);
guest_handle_add_offset(nat_ops, i - left);
guest_handle_subtract_offset(cmp_uops, left);
+1
View File
@@ -405,6 +405,7 @@ struct arch_vcpu
pagetable_t guest_table_user; /* (MFN) x86/64 user-space pagetable */
pagetable_t guest_table; /* (MFN) guest notion of cr3 */
struct page_info *old_guest_table; /* partially destructed pagetable */
/* guest_table holds a ref to the page, and also a type-count unless
* shadow refcounts are in use */
pagetable_t shadow_table[4]; /* (MFN) shadow(s) of guest */
+1
View File
@@ -556,6 +556,7 @@ void audit_domains(void);
int new_guest_cr3(unsigned long pfn);
void make_cr3(struct vcpu *v, unsigned long mfn);
void update_cr3(struct vcpu *v);
int vcpu_destroy_pagetables(struct vcpu *, bool_t preemptible);
void propagate_page_fault(unsigned long addr, u16 error_code);
void *do_page_walk(struct vcpu *v, unsigned long addr);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.