mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
x86/mm: Plumbing to allow any PTE update to fail with -ERESTART
Switching to shadow mode is performed in tasklet context. To facilitate this,
we schedule the tasklet, then create a hypercall continuation to allow the
switch to take place.
As a consequence, the x86 mm code needs to cope with an L1e operation being
continuable. do_mmu{,ext}_op() may no longer assert that a continuation
doesn't happen on the final iteration.
To handle the arguments correctly on continuation, compat_update_va_mapping*()
may no longer call into their non-compat counterparts. Move the compat
functions into mm.c rather than exporting __do_update_va_mapping() and
{get,put}_pg_owner(), and fix an unsigned long/int inconsistency with
compat_update_va_mapping_otherdomain().
This is part of XSA-273 / CVE-2018-3620.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
b76ec3946b
commit
c612481d1c
3 files changed
+66
-32
No files matched your search
+65
-18
@@ -613,6 +613,9 @@ static int alloc_segdesc_page(struct page_info *page)
|
||||
return i == 512 ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int _get_page_type(struct page_info *page, unsigned long type,
|
||||
bool preemptible);
|
||||
|
||||
static int get_page_and_type_from_mfn(
|
||||
mfn_t mfn, unsigned long type, struct domain *d,
|
||||
int partial, int preemptible)
|
||||
@@ -624,9 +627,7 @@ static int get_page_and_type_from_mfn(
|
||||
unlikely(!get_page_from_mfn(mfn, d)) )
|
||||
return -EINVAL;
|
||||
|
||||
rc = (preemptible ?
|
||||
get_page_type_preemptible(page, type) :
|
||||
(get_page_type(page, type) ? 0 : -EINVAL));
|
||||
rc = _get_page_type(page, type, preemptible);
|
||||
|
||||
if ( unlikely(rc) && partial >= 0 &&
|
||||
(!preemptible || page != current->arch.old_guest_table) )
|
||||
@@ -1456,8 +1457,7 @@ static int create_pae_xen_mappings(struct domain *d, l3_pgentry_t *pl3e)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int alloc_l2_table(struct page_info *page, unsigned long type,
|
||||
int preemptible)
|
||||
static int alloc_l2_table(struct page_info *page, unsigned long type)
|
||||
{
|
||||
struct domain *d = page_get_owner(page);
|
||||
unsigned long pfn = mfn_x(page_to_mfn(page));
|
||||
@@ -1469,8 +1469,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type,
|
||||
|
||||
for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; i++ )
|
||||
{
|
||||
if ( preemptible && i > page->nr_validated_ptes
|
||||
&& hypercall_preempt_check() )
|
||||
if ( i > page->nr_validated_ptes && hypercall_preempt_check() )
|
||||
{
|
||||
page->nr_validated_ptes = i;
|
||||
rc = -ERESTART;
|
||||
@@ -1481,6 +1480,12 @@ static int alloc_l2_table(struct page_info *page, unsigned long type,
|
||||
(rc = get_page_from_l2e(pl2e[i], pfn, d)) > 0 )
|
||||
continue;
|
||||
|
||||
if ( unlikely(rc == -ERESTART) )
|
||||
{
|
||||
page->nr_validated_ptes = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( rc < 0 )
|
||||
{
|
||||
gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i);
|
||||
@@ -1763,7 +1768,7 @@ static void free_l1_table(struct page_info *page)
|
||||
}
|
||||
|
||||
|
||||
static int free_l2_table(struct page_info *page, int preemptible)
|
||||
static int free_l2_table(struct page_info *page)
|
||||
{
|
||||
struct domain *d = page_get_owner(page);
|
||||
unsigned long pfn = mfn_x(page_to_mfn(page));
|
||||
@@ -1777,7 +1782,7 @@ static int free_l2_table(struct page_info *page, int preemptible)
|
||||
do {
|
||||
if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) &&
|
||||
put_page_from_l2e(pl2e[i], pfn) == 0 &&
|
||||
preemptible && i && hypercall_preempt_check() )
|
||||
i && hypercall_preempt_check() )
|
||||
{
|
||||
page->nr_validated_ptes = i;
|
||||
err = -ERESTART;
|
||||
@@ -2373,7 +2378,8 @@ static int alloc_page_type(struct page_info *page, unsigned long type,
|
||||
rc = alloc_l1_table(page);
|
||||
break;
|
||||
case PGT_l2_page_table:
|
||||
rc = alloc_l2_table(page, type, preemptible);
|
||||
ASSERT(preemptible);
|
||||
rc = alloc_l2_table(page, type);
|
||||
break;
|
||||
case PGT_l3_page_table:
|
||||
ASSERT(preemptible);
|
||||
@@ -2463,7 +2469,8 @@ int free_page_type(struct page_info *page, unsigned long type,
|
||||
rc = 0;
|
||||
break;
|
||||
case PGT_l2_page_table:
|
||||
rc = free_l2_table(page, preemptible);
|
||||
ASSERT(preemptible);
|
||||
rc = free_l2_table(page);
|
||||
break;
|
||||
case PGT_l3_page_table:
|
||||
ASSERT(preemptible);
|
||||
@@ -3550,12 +3557,9 @@ long do_mmuext_op(
|
||||
}
|
||||
|
||||
if ( rc == -ERESTART )
|
||||
{
|
||||
ASSERT(i < count);
|
||||
rc = hypercall_create_continuation(
|
||||
__HYPERVISOR_mmuext_op, "hihi",
|
||||
uops, (count - i) | MMU_UPDATE_PREEMPTED, pdone, foreigndom);
|
||||
}
|
||||
else if ( curr->arch.old_guest_table )
|
||||
{
|
||||
XEN_GUEST_HANDLE_PARAM(void) null;
|
||||
@@ -3861,12 +3865,9 @@ long do_mmu_update(
|
||||
}
|
||||
|
||||
if ( rc == -ERESTART )
|
||||
{
|
||||
ASSERT(i < count);
|
||||
rc = hypercall_create_continuation(
|
||||
__HYPERVISOR_mmu_update, "hihi",
|
||||
ureqs, (count - i) | MMU_UPDATE_PREEMPTED, pdone, foreigndom);
|
||||
}
|
||||
else if ( curr->arch.old_guest_table )
|
||||
{
|
||||
XEN_GUEST_HANDLE_PARAM(void) null;
|
||||
@@ -4121,7 +4122,13 @@ static int __do_update_va_mapping(
|
||||
long do_update_va_mapping(unsigned long va, u64 val64,
|
||||
unsigned long flags)
|
||||
{
|
||||
return __do_update_va_mapping(va, val64, flags, current->domain);
|
||||
int rc = __do_update_va_mapping(va, val64, flags, current->domain);
|
||||
|
||||
if ( rc == -ERESTART )
|
||||
rc = hypercall_create_continuation(
|
||||
__HYPERVISOR_update_va_mapping, "lll", va, val64, flags);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
long do_update_va_mapping_otherdomain(unsigned long va, u64 val64,
|
||||
@@ -4138,6 +4145,46 @@ long do_update_va_mapping_otherdomain(unsigned long va, u64 val64,
|
||||
|
||||
put_pg_owner(pg_owner);
|
||||
|
||||
if ( rc == -ERESTART )
|
||||
rc = hypercall_create_continuation(
|
||||
__HYPERVISOR_update_va_mapping_otherdomain,
|
||||
"llli", va, val64, flags, domid);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int compat_update_va_mapping(unsigned int va, uint32_t lo, uint32_t hi,
|
||||
unsigned int flags)
|
||||
{
|
||||
int rc = __do_update_va_mapping(va, ((uint64_t)hi << 32) | lo,
|
||||
flags, current->domain);
|
||||
|
||||
if ( rc == -ERESTART )
|
||||
rc = hypercall_create_continuation(
|
||||
__HYPERVISOR_update_va_mapping, "iiii", va, lo, hi, flags);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int compat_update_va_mapping_otherdomain(unsigned int va,
|
||||
uint32_t lo, uint32_t hi,
|
||||
unsigned int flags, domid_t domid)
|
||||
{
|
||||
struct domain *pg_owner;
|
||||
int rc;
|
||||
|
||||
if ( (pg_owner = get_pg_owner(domid)) == NULL )
|
||||
return -ESRCH;
|
||||
|
||||
rc = __do_update_va_mapping(va, ((uint64_t)hi << 32) | lo, flags, pg_owner);
|
||||
|
||||
put_pg_owner(pg_owner);
|
||||
|
||||
if ( rc == -ERESTART )
|
||||
rc = hypercall_create_continuation(
|
||||
__HYPERVISOR_update_va_mapping_otherdomain,
|
||||
"iiiii", va, lo, hi, flags, domid);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,19 +163,6 @@ int compat_arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int compat_update_va_mapping(unsigned int va, u32 lo, u32 hi,
|
||||
unsigned int flags)
|
||||
{
|
||||
return do_update_va_mapping(va, lo | ((u64)hi << 32), flags);
|
||||
}
|
||||
|
||||
int compat_update_va_mapping_otherdomain(unsigned long va, u32 lo, u32 hi,
|
||||
unsigned long flags,
|
||||
domid_t domid)
|
||||
{
|
||||
return do_update_va_mapping_otherdomain(va, lo | ((u64)hi << 32), flags, domid);
|
||||
}
|
||||
|
||||
DEFINE_XEN_GUEST_HANDLE(mmuext_op_compat_t);
|
||||
|
||||
int compat_mmuext_op(XEN_GUEST_HANDLE_PARAM(void) arg,
|
||||
|
||||
@@ -165,7 +165,7 @@ extern int compat_update_va_mapping(
|
||||
unsigned int va, u32 lo, u32 hi, unsigned int flags);
|
||||
|
||||
extern int compat_update_va_mapping_otherdomain(
|
||||
unsigned long va, u32 lo, u32 hi, unsigned long flags, domid_t domid);
|
||||
unsigned int va, u32 lo, u32 hi, unsigned int flags, domid_t domid);
|
||||
|
||||
DEFINE_XEN_GUEST_HANDLE(trap_info_compat_t);
|
||||
extern int compat_set_trap_table(XEN_GUEST_HANDLE(trap_info_compat_t) traps);
|
||||
|
||||