]> xenbits.xensource.com Git - xen.git/commitdiff
x86: limit GFNs to 32 bits for shadowed superpages.
authorTim Deegan <tim@xen.org>
Wed, 16 Mar 2016 16:56:04 +0000 (16:56 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 20 Apr 2016 17:05:20 +0000 (18:05 +0100)
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 <liuling-it@360.cn>
Signed-off-by: Tim Deegan <tim@xen.org>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/cpu/common.c
xen/arch/x86/hvm/hvm.c
xen/arch/x86/mm/guest_walk.c
xen/arch/x86/mm/hap/hap.c
xen/arch/x86/mm/shadow/common.c
xen/arch/x86/mm/shadow/multi.c
xen/include/asm-x86/domain.h
xen/include/asm-x86/guest_pt.h
xen/include/asm-x86/processor.h
xen/include/asm-x86/x86_64/page.h

index 5c8d3c251e2d31b0fa40ea8826f43a7d0d5070ff..7dc82200b0b31ea718856535555a51f60f2110d1 100644 (file)
@@ -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.
@@ -209,7 +210,7 @@ static void __init early_cpu_detect(void)
 
 static void __cpuinit generic_identify(struct cpuinfo_x86 *c)
 {
-       u32 tfms, capability, excap, ebx;
+       u32 tfms, capability, excap, ebx, eax;
 
        /* Get vendor name */
        cpuid(0x00000000, &c->cpuid_level,
@@ -246,8 +247,11 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 *c)
                }
                if ( c->extended_cpuid_level >= 0x80000004 )
                        get_model_name(c); /* Default name */
-               if ( c->extended_cpuid_level >= 0x80000008 )
-                       paddr_bits = cpuid_eax(0x80000008) & 0xff;
+               if ( c->extended_cpuid_level >= 0x80000008 ) {
+                       eax = cpuid_eax(0x80000008);
+                       paddr_bits = eax & 0xff;
+                       hap_paddr_bits = ((eax >> 16) & 0xff) ?: paddr_bits;
+               }
        }
 
        /* Might lift BIOS max_leaf=3 limit. */
index 91518486cb401a6aba241dfd8288d397c064f7b0..cf527a2df5be447356566a3e6d6d91c1983bd918 100644 (file)
@@ -4328,8 +4328,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;
 
index 1b26175a881825d221bf7da3c77a75b980337cde..50ba7d5afa98e65bcd7902e4b53ebe0a4f734b81 100644 (file)
@@ -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);
@@ -327,20 +333,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) +
@@ -423,5 +417,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;
 }
index 0c8001233f51b53a8f10dee8d30e9ee800fd546d..84531b1f2316f585f725944db729780218716a12 100644 (file)
@@ -429,6 +429,8 @@ 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;
+
     /* Use HAP logdirty mechanism. */
     paging_log_dirty_init(d, hap_enable_log_dirty,
                           hap_disable_log_dirty,
index f3b64f1186c065e891093ea4b97115c02d72a911..76a56ca6590f6d78fbaa2d4c0f75b4bf6e6f6925 100644 (file)
@@ -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);
index d6802ff29a4324fe4e53ea61e6f8f4a7f3f62c3b..7589d23eaa1bf6eb7165a0bbf450e1861f979264 100644 (file)
@@ -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;
index 6a77a930ad7ff8e608aeb0c7c7d24d786d229176..e8df4a9ff3ee9ea9d6095ecb1b7379dc08aa520d 100644 (file)
@@ -188,6 +188,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;
index d2a82506ed279967b595478e29dbb3e0f333ff9e..d95f8352cbbedb015f9c07d989922767e5f1164a 100644 (file)
@@ -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<<paddr_bits) - 1))
-#else /* GUEST_PAGING_LEVELS == 4 */
+/*
+ * Some bits are invalid in any pagetable entry.
+ * Normal flags values get represented in 24-bit values (see
+ * get_pte_flags() and put_pte_flags()), so set bit 24 in
+ * addition to be able to flag out of range frame numbers.
+ */
+#if GUEST_PAGING_LEVELS == 3
 #define _PAGE_INVALID_BITS \
-    get_pte_flags(((1ull<<52) - 1) & ~((1ull<<paddr_bits) - 1))
+    (_PAGE_INVALID_BIT | get_pte_flags(((1ull << 63) - 1) & ~(PAGE_SIZE - 1)))
+#else /* 2-level and 4-level */
+#define _PAGE_INVALID_BITS _PAGE_INVALID_BIT
 #endif
 
 
index b4e4731ab14e90894e113bcc129b81aa08db1993..56fc5a2816927d21d3c95f9d165f4e7a650b5fc4 100644 (file)
@@ -203,6 +203,8 @@ extern u32 cpuid_ext_features;
 
 /* Maximum width of physical addresses supported by the hardware */
 extern unsigned int paddr_bits;
+/* Max physical address width supported within HAP guests */
+extern unsigned int hap_paddr_bits;
 
 extern void identify_cpu(struct cpuinfo_x86 *);
 extern void setup_clear_cpu_cap(unsigned int);
index 1d54587b29c2854e3115d9928db580b43713bd64..f1d1b6c8de8936326f4b7b1e195c6d8d092da5ba 100644 (file)
@@ -140,6 +140,12 @@ typedef l4_pgentry_t root_pgentry_t;
 /* Bit 22 of a 24-bit flag mask. This corresponds to bit 62 of a pte.*/
 #define _PAGE_GNTTAB (1U<<22)
 
+/*
+ * Bit 24 of a 24-bit flag mask!  This is not any bit of a real pte,
+ * and is only used for signalling in variables that contain flags.
+ */
+#define _PAGE_INVALID_BIT (1U<<24)
+
 /*
  * Bit 12 of a 24-bit flag mask. This corresponds to bit 52 of a pte.
  * This is needed to distinguish between user and kernel PTEs since _PAGE_USER