x86: limit GFNs to 32 bits for shadowed superpages.
authorTim Deegan <tim@xen.org>
Mon, 14 Mar 2016 11:05:48 +0000 (11:05 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 20 Apr 2016 17:01:01 +0000 (18:01 +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/p2m.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 fe6eab49cc542dea65c74768a4f6f371e7de1d63..f664341203f0454717c1a98b502a109a365549cb 100644 (file)
@@ -45,6 +45,7 @@ struct cpuidmasks __read_mostly cpuidmask_defaults;
 const struct cpu_dev *__read_mostly 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.
@@ -236,8 +237,11 @@ static void __init early_cpu_detect(void)
        c->x86_capability[cpufeat_word(X86_FEATURE_FPU)] = edx;
        c->x86_capability[cpufeat_word(X86_FEATURE_SSE3)] = ecx;
 
-       if ( cpuid_eax(0x80000000) >= 0x80000008 )
-               paddr_bits = cpuid_eax(0x80000008) & 0xff;
+       if ( cpuid_eax(0x80000000) >= 0x80000008 ) {
+               eax = cpuid_eax(0x80000008);
+               paddr_bits = eax & 0xff;
+               hap_paddr_bits = ((eax >> 16) & 0xff) ?: paddr_bits;
+       }
 }
 
 static void generic_identify(struct cpuinfo_x86 *c)
index f24126d8c834fb73f3965c7be73cd4fe63107f62..e9d4c6b06ce8fb52f60586116a867542939c9f4f 100644 (file)
@@ -3504,8 +3504,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 21cabbab15a7226bce256022778d879d53dbbf00..625ac4dfcc241295a708c6dfbf7cb43354476a35 100644 (file)
@@ -342,20 +342,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) +
@@ -441,5 +429,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 f17353954987f86d41f972fe135b59c86c7a8006..4ab99bb607747a9d2641c96ed39b5d03a3f0c7c2 100644 (file)
@@ -448,6 +448,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 b3fce1b7abf07d419215d8264ece6a538b9ac387..9e82256f3d12ecedaab8f065e18cad50505a36e3 100644 (file)
@@ -2081,6 +2081,12 @@ void *map_domain_gfn(struct p2m_domain *p2m, gfn_t gfn, mfn_t *mfn,
 {
     struct page_info *page;
 
+    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);
     if ( p2m_is_paging(*p2mt) )
index ec87fb4b510c92ea7cb51d6fb85d971e5afa42dd..4e06c1364f1d51ace281dba9d1d805c2f0100546 100644 (file)
@@ -51,6 +51,16 @@ int 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, sh_enable_log_dirty,
                           sh_disable_log_dirty, sh_clean_dirty_bitmap);
index e5c8499a6653f2d2719f6094d1b0e1a6fe27e762..428be374b802cc028dde6ab3de586b2e4f1539c9 100644 (file)
@@ -540,7 +540,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 d393ed24aa86a90c442fabc7e5c31fb42e161909..165e533ab3f74b3daba52dacb7294915aafc9863 100644 (file)
@@ -194,6 +194,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 eb29e622f6a94c42ae55110d067933ace76b0380..1f6c2ae357fa000cc1b1105573437f508c002114 100644 (file)
@@ -222,15 +222,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 f29d37046fd3f71f9d9717f804f72327f9aa84fe..37ac9e46ab8b24bb63f86d684d39c941984b3dd9 100644 (file)
@@ -218,6 +218,8 @@ extern u64 trampoline_misc_enable_off;
 
 /* 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 const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id table[]);
 
index 86abb94628e4b65a4b77f9cef0ae4b826fe5c84e..589f22552e6feee5588d9c688bd7a9731343c5c5 100644 (file)
@@ -152,6 +152,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