From 24ebffa9f57a14b6f20376ae422b941715af9a4e Mon Sep 17 00:00:00 2001 From: Tim Deegan Date: Wed, 16 Mar 2016 17:05:25 +0000 Subject: [PATCH] x86: limit GFNs to 32 bits for shadowed superpages. Superpage shadows store the shadowed GFN in the backpointer field, which for non-BIGMEM builds is 32 bits wide. Shadowing a superpage mapping of a guest-physical address above 2^44 would lead to the GFN being truncated there, and a crash when we come to remove the shadow from the hash table. Track the valid width of a GFN for each guest, including reporting it through CPUID, and enforce it in the shadow pagetables. Set the maximum witth to 32 for guests where this truncation could occur. This is XSA-173. Reported-by: Ling Liu Signed-off-by: Tim Deegan Signed-off-by: Jan Beulich --- xen/arch/x86/cpu/common.c | 10 +++++++--- xen/arch/x86/hvm/hvm.c | 3 +-- xen/arch/x86/mm/guest_walk.c | 26 +++++++++++++------------- xen/arch/x86/mm/hap/hap.c | 1 + xen/arch/x86/mm/shadow/common.c | 10 ++++++++++ xen/arch/x86/mm/shadow/multi.c | 3 ++- xen/include/asm-x86/domain.h | 3 +++ xen/include/asm-x86/guest_pt.h | 18 ++++++++++-------- xen/include/asm-x86/processor.h | 2 ++ xen/include/asm-x86/x86_64/page.h | 7 +++++++ 10 files changed, 56 insertions(+), 27 deletions(-) diff --git a/xen/arch/x86/cpu/common.c b/xen/arch/x86/cpu/common.c index 4221826ec0..f436f91804 100644 --- a/xen/arch/x86/cpu/common.c +++ b/xen/arch/x86/cpu/common.c @@ -37,6 +37,7 @@ integer_param("cpuid_mask_ext_edx", opt_cpuid_mask_ext_edx); struct cpu_dev * cpu_devs[X86_VENDOR_NUM] = {}; unsigned int paddr_bits __read_mostly = 36; +unsigned int hap_paddr_bits __read_mostly = 36; /* * Default host IA32_CR_PAT value to cover all memory types. @@ -195,7 +196,7 @@ static void __init early_cpu_detect(void) static void __cpuinit generic_identify(struct cpuinfo_x86 *c) { - u32 tfms, xlvl, capability, excap, ebx; + u32 tfms, xlvl, capability, excap, eax, ebx; /* Get vendor name */ cpuid(0x00000000, &c->cpuid_level, @@ -230,8 +231,11 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 *c) } if ( xlvl >= 0x80000004 ) get_model_name(c); /* Default name */ - if ( xlvl >= 0x80000008 ) - paddr_bits = cpuid_eax(0x80000008) & 0xff; + if ( xlvl >= 0x80000008 ) { + eax = cpuid_eax(0x80000008); + paddr_bits = eax & 0xff; + hap_paddr_bits = ((eax >> 16) & 0xff) ?: paddr_bits; + } } /* Might lift BIOS max_leaf=3 limit. */ diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index f3f6c61ae2..a4bfb9013b 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -2966,8 +2966,7 @@ void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx, break; case 0x80000008: - count = cpuid_eax(0x80000008); - count = (count >> 16) & 0xff ?: count & 0xff; + count = d->arch.paging.gfn_bits + PAGE_SHIFT; if ( (*eax & 0xff) > count ) *eax = (*eax & ~0xff) | count; diff --git a/xen/arch/x86/mm/guest_walk.c b/xen/arch/x86/mm/guest_walk.c index 70460b6ad7..09511f0a79 100644 --- a/xen/arch/x86/mm/guest_walk.c +++ b/xen/arch/x86/mm/guest_walk.c @@ -94,6 +94,12 @@ void *map_domain_gfn(struct p2m_domain *p2m, gfn_t gfn, mfn_t *mfn, struct page_info *page; void *map; + if ( gfn_x(gfn) >> p2m->domain->arch.paging.gfn_bits ) + { + *rc = _PAGE_INVALID_BIT; + return NULL; + } + /* Translate the gfn, unsharing if shared */ page = get_page_from_gfn_p2m(p2m->domain, p2m, gfn_x(gfn), p2mt, NULL, q); @@ -294,20 +300,8 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, flags &= ~_PAGE_PAT; if ( gfn_x(start) & GUEST_L2_GFN_MASK & ~0x1 ) - { -#if GUEST_PAGING_LEVELS == 2 - /* - * Note that _PAGE_INVALID_BITS is zero in this case, yielding a - * no-op here. - * - * Architecturally, the walk should fail if bit 21 is set (others - * aren't being checked at least in PSE36 mode), but we'll ignore - * this here in order to avoid specifying a non-natural, non-zero - * _PAGE_INVALID_BITS value just for that case. - */ -#endif rc |= _PAGE_INVALID_BITS; - } + /* Increment the pfn by the right number of 4k pages. * Mask out PAT and invalid bits. */ start = _gfn((gfn_x(start) & ~GUEST_L2_GFN_MASK) + @@ -390,5 +384,11 @@ set_ad: put_page(mfn_to_page(mfn_x(gw->l1mfn))); } + /* If this guest has a restricted physical address space then the + * target GFN must fit within it. */ + if ( !(rc & _PAGE_PRESENT) + && gfn_x(guest_l1e_get_gfn(gw->l1e)) >> d->arch.paging.gfn_bits ) + rc |= _PAGE_INVALID_BITS; + return rc; } diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c index c06369b10d..ccc4174716 100644 --- a/xen/arch/x86/mm/hap/hap.c +++ b/xen/arch/x86/mm/hap/hap.c @@ -428,6 +428,7 @@ static void hap_destroy_monitor_table(struct vcpu* v, mfn_t mmfn) void hap_domain_init(struct domain *d) { INIT_PAGE_LIST_HEAD(&d->arch.paging.hap.freelist); + d->arch.paging.gfn_bits = hap_paddr_bits - PAGE_SHIFT; } /* return 0 for success, -errno for failure */ diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index 90ba4d6d32..06a04addc1 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -48,6 +48,16 @@ void shadow_domain_init(struct domain *d, unsigned int domcr_flags) INIT_PAGE_LIST_HEAD(&d->arch.paging.shadow.freelist); INIT_PAGE_LIST_HEAD(&d->arch.paging.shadow.pinned_shadows); + d->arch.paging.gfn_bits = paddr_bits - PAGE_SHIFT; +#ifndef CONFIG_BIGMEM + /* + * Shadowed superpages store GFNs in 32-bit page_info fields. + * Note that we cannot use guest_supports_superpages() here. + */ + if ( !is_pv_domain(d) || opt_allow_superpage ) + d->arch.paging.gfn_bits = 32; +#endif + /* Use shadow pagetables for log-dirty support */ paging_log_dirty_init(d, shadow_enable_log_dirty, shadow_disable_log_dirty, shadow_clean_dirty_bitmap); diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index 0f499bf3e2..6c88d4e94b 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -527,7 +527,8 @@ _sh_propagate(struct vcpu *v, ASSERT(GUEST_PAGING_LEVELS > 3 || level != 3); /* Check there's something for the shadows to map to */ - if ( !p2m_is_valid(p2mt) && !p2m_is_grant(p2mt) ) + if ( (!p2m_is_valid(p2mt) && !p2m_is_grant(p2mt)) + || gfn_x(target_gfn) >> d->arch.paging.gfn_bits ) { *sp = shadow_l1e_empty(); goto done; diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index 7dfbbcb787..a03fc2ed17 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -187,6 +187,9 @@ struct paging_domain { /* log dirty support */ struct log_dirty_domain log_dirty; + /* Number of valid bits in a gfn. */ + unsigned int gfn_bits; + /* preemption handling */ struct { const struct domain *dom; diff --git a/xen/include/asm-x86/guest_pt.h b/xen/include/asm-x86/guest_pt.h index d2a82506ed..d95f8352cb 100644 --- a/xen/include/asm-x86/guest_pt.h +++ b/xen/include/asm-x86/guest_pt.h @@ -220,15 +220,17 @@ guest_supports_nx(struct vcpu *v) } -/* Some bits are invalid in any pagetable entry. */ -#if GUEST_PAGING_LEVELS == 2 -#define _PAGE_INVALID_BITS (0) -#elif GUEST_PAGING_LEVELS == 3 -#define _PAGE_INVALID_BITS \ - get_pte_flags(((1ull<<63) - 1) & ~((1ull<