|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen stable-4.8] x86/mm: Plumbing to allow any PTE update to fail with -ERESTART
commit 7849d13d456916138d1b79fe92b0f8ee2fd60429
Author: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
AuthorDate: Mon Jul 23 08:11:40 2018 +0200
Commit: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Tue Aug 14 17:27:27 2018 +0100
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@xxxxxxxxxx>
Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
(cherry picked from commit c612481d1c9232c6abf91b03ec655e92f808805f)
---
xen/arch/x86/mm.c | 80 +++++++++++++++++++++++++++++++----------
xen/arch/x86/x86_64/compat/mm.c | 13 -------
xen/include/asm-x86/hypercall.h | 2 +-
3 files changed, 63 insertions(+), 32 deletions(-)
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index f439fc4e74..52f0d6bde9 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -747,6 +747,8 @@ static int get_page_from_pagenr(unsigned long page_nr,
struct domain *d)
return 1;
}
+static int __get_page_type(struct page_info *page, unsigned long type,
+ int preemptible);
static int get_page_and_type_from_pagenr(unsigned long page_nr,
unsigned long type,
@@ -761,9 +763,7 @@ static int get_page_and_type_from_pagenr(unsigned long
page_nr,
unlikely(!get_page_from_pagenr(page_nr, 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) )
@@ -1610,8 +1610,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 = page_to_mfn(page);
@@ -1623,8 +1622,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;
@@ -1635,6 +1633,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 )
{
MEM_LOG("Failure in alloc_l2_table: entry %d", i);
@@ -1859,7 +1863,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 = page_to_mfn(page);
@@ -1873,7 +1877,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;
@@ -2478,7 +2482,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);
@@ -2569,7 +2574,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);
@@ -3859,12 +3865,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;
@@ -4155,12 +4158,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;
@@ -4869,7 +4869,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,
@@ -4886,10 +4892,48 @@ 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;
+}
/*************************
* Descriptor Tables
diff --git a/xen/arch/x86/x86_64/compat/mm.c b/xen/arch/x86/x86_64/compat/mm.c
index 58be8ad7f1..41a0921f7c 100644
--- a/xen/arch/x86/x86_64/compat/mm.c
+++ b/xen/arch/x86/x86_64/compat/mm.c
@@ -200,19 +200,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,
diff --git a/xen/include/asm-x86/hypercall.h b/xen/include/asm-x86/hypercall.h
index c59aa69b14..7c3f46e183 100644
--- a/xen/include/asm-x86/hypercall.h
+++ b/xen/include/asm-x86/hypercall.h
@@ -158,7 +158,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);
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.8
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/xen-changelog
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |