[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen staging-4.7] x86/mm: Plumbing to allow any PTE update to fail with -ERESTART
commit 0abc1aee7c1cbed1233d16fea96f85d63cf18122 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:28:58 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 ------- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 38d0617b85..ef7e6b7ff2 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -746,6 +746,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, @@ -760,9 +762,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) ) @@ -1609,8 +1609,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); @@ -1622,8 +1621,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; @@ -1634,6 +1632,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); @@ -1858,7 +1862,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); @@ -1872,7 +1876,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; @@ -2477,7 +2481,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); @@ -2568,7 +2573,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); @@ -3855,12 +3861,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; @@ -4151,12 +4154,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; @@ -4864,7 +4864,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, @@ -4881,10 +4887,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, -- generated by git-patchbot for /home/xen/git/xen.git#staging-4.7 _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |