[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen stable-4.11] x86/mm: Plumbing to allow any PTE update to fail with -ERESTART
commit f4a049ede7ee9e1fafad6248cffc5e6deac1bc39 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:15:22 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 | 83 ++++++++++++++++++++++++++++++++--------- xen/arch/x86/x86_64/compat/mm.c | 13 ------- xen/include/asm-x86/hypercall.h | 2 +- 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index bcf46c0743..657af50c4c 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -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; } diff --git a/xen/arch/x86/x86_64/compat/mm.c b/xen/arch/x86/x86_64/compat/mm.c index c2aa6f2fdb..02bc75b91e 100644 --- a/xen/arch/x86/x86_64/compat/mm.c +++ b/xen/arch/x86/x86_64/compat/mm.c @@ -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, diff --git a/xen/include/asm-x86/hypercall.h b/xen/include/asm-x86/hypercall.h index 1cc2e37d5c..da38b7991c 100644 --- a/xen/include/asm-x86/hypercall.h +++ b/xen/include/asm-x86/hypercall.h @@ -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); -- generated by git-patchbot for /home/xen/git/xen.git#stable-4.11 _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |