[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] x86/mm: Make PV linear pagetables optional



Allowing pagetables to point to other pagetables of the same level
(often called 'linear pagetables') has been included in Xen since its
inception; but recently it has been the source of a number of subtle
reference-counting bugs.

It is not used by Linux or MiniOS; but it used used by NetBSD and
Novell Netware.  There are significant numbers of people who are never
going to use the feature, along with significant numbers who need the
feature.

Add a Kconfig option for the feature (default to 'y').  Also add a
command-line option to control whether PV linear pagetables are
allowed (default to 'true').

In order to make the code clean:
- Introduce LPT_ASSERT(), which only exists if CONFIG_PV_LINEAR_PT is defined
- Introduce zero_linear_entries() to set page->linear_pt_count to zero
  (or do nothing, as appropriate)

Reported-by: Jann Horn <jannh@xxxxxxxxxx>
Signed-off-by: George Dunlap <george.dunlap@xxxxxxxxxx>
---
Changes since XSA
- Add a Kconfig option
- Default to 'on' (rather than 'off').

Release justification: This was originally part of a security fix
embargoed until after the freeze date; it wasn't checked in with the
other security patches in order to allow a discussion about the
default.

CC: Ian Jackson <ian.jackson@xxxxxxxxxx>
CC: Wei Liu <wei.liu2@xxxxxxxxxx>
CC: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CC: Jan Beulich <jbeulich@xxxxxxxx>
CC: Stefano Stabellini <sstabellini@xxxxxxxxxx>
CC: Konrad Wilk <konrad.wilk@xxxxxxxxxx>
CC: Julien Grall <julien.grall@xxxxxxx>
---
 docs/misc/xen-command-line.markdown | 16 ++++++++++++++++
 xen/arch/Kconfig                    |  1 +
 xen/arch/arm/mm.c                   |  1 +
 xen/arch/x86/Kconfig                | 21 ++++++++++++++++++++
 xen/arch/x86/mm.c                   | 38 +++++++++++++++++++++++++++++++++----
 xen/include/asm-x86/mm.h            |  5 +++++
 6 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/docs/misc/xen-command-line.markdown 
b/docs/misc/xen-command-line.markdown
index eb4995e68b..952368d3be 100644
--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -1422,6 +1422,22 @@ The following resources are available:
     CDP, one COS will corespond two CBMs other than one with CAT, due to the
     sum of CBMs is fixed, that means actual `cos_max` in use will automatically
     reduce to half when CDP is enabled.
+       
+### pv-linear-pt
+> `= <boolean>`
+
+> Default: `false`
+
+Allow PV guests to have pagetable entries pointing to other pagetables
+of the same level (i.e., allowing L2 PTEs to point to other L2 pages).
+This technique is often called "linear pagetables", and is sometimes
+used to allow operating systems a simple way to consistently map the
+current process's pagetables into its own virtual address space.
+
+Linux and MiniOS don't use this technique.  NetBSD and Novell Netware
+do; there may be other custom operating systems which do.  If you're
+certain you don't plan on having PV guests which use this feature,
+turning it off can reduce the attack surface.
 
 ### rcu-idle-timer-period-ms
 > `= <integer>`
diff --git a/xen/arch/Kconfig b/xen/arch/Kconfig
index cf0acb7e89..47287a4985 100644
--- a/xen/arch/Kconfig
+++ b/xen/arch/Kconfig
@@ -6,3 +6,4 @@ config NR_CPUS
        default "128" if ARM
        ---help---
          Specifies the maximum number of physical CPUs which Xen will support.
+
diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index 3c328e2df5..199155fcd8 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -42,6 +42,7 @@
 #include <xen/libfdt/libfdt.h>
 #include <asm/setup.h>
 
+
 struct domain *dom_xen, *dom_io, *dom_cow;
 
 /* Override macros from asm/page.h to make them work with mfn_t */
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index 64955dc017..e2fcbaf5cc 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -97,6 +97,27 @@ config TBOOT
          Technology (TXT)
 
          If unsure, say Y.
+
+config PV_LINEAR_PT
+       bool "Support for PV linear pagetables"
+       depends on PV
+       default y
+       ---help---
+         Linear pagetables (also called "recursive pagetables") refers
+        to the practice of a guest operating system having pagetable
+        entries pointing to other pagetables of the same level (i.e.,
+        allowing L2 PTEs to point to other L2 pages).  Some operating
+        systems use it as a simple way to consisently map the current
+        process's pagetables into its own virtual address space.
+
+        Linux and MiniOS don't use this technique.  NetBSD and Novell
+        Netware do; there may be other custom operating systems which
+        do.  If you're certain you don't plan on having PV guests
+        which use this feature, turning it off can reduce the attack
+        surface.
+
+        If unsure, say Y.
+
 endmenu
 
 source "common/Kconfig"
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 62d313e3f5..5881b64608 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -587,6 +587,12 @@ static void put_data_page(
         put_page(page);
 }
 
+#ifdef CONFIG_PV_LINEAR_PT
+static void zero_linear_entries(struct page_info *pg)
+{
+    pg->linear_pt_count = 0;
+}
+
 static bool inc_linear_entries(struct page_info *pg)
 {
     typeof(pg->linear_pt_count) nc = read_atomic(&pg->linear_pt_count), oc;
@@ -654,6 +660,9 @@ static void dec_linear_uses(struct page_info *pg)
  *     frame if it is mapped by a different root table. This is sufficient and
  *     also necessary to allow validation of a root table mapping itself.
  */
+static bool __read_mostly pv_linear_pt_enable = true;
+boolean_param("pv-linear-pt", pv_linear_pt_enable);
+
 #define define_get_linear_pagetable(level)                                  \
 static int                                                                  \
 get_##level##_linear_pagetable(                                             \
@@ -663,6 +672,13 @@ get_##level##_linear_pagetable(                            
                 \
     struct page_info *page;                                                 \
     unsigned long pfn;                                                      \
                                                                             \
+    if ( !pv_linear_pt_enable )                                             \
+    {                                                                       \
+        gdprintk(XENLOG_WARNING,                                            \
+                 "Attempt to create linear p.t. (feature disabled)\n");     \
+        return 0;                                                           \
+    }                                                                       \
+                                                                            \
     if ( (level##e_get_flags(pde) & _PAGE_RW) )                             \
     {                                                                       \
         gdprintk(XENLOG_WARNING,                                            \
@@ -719,6 +735,20 @@ get_##level##_linear_pagetable(                            
                 \
                                                                             \
     return 1;                                                               \
 }
+#define LPT_ASSERT ASSERT
+#else
+#define define_get_linear_pagetable(level)                              \
+static int                                                              \
+get_##level##_linear_pagetable(                                         \
+        level##_pgentry_t pde, unsigned long pde_pfn, struct domain *d) \
+{                                                                       \
+        return 0;                                                       \
+}
+#define zero_linear_entries(pg)
+#define dec_linear_uses(pg)
+#define dec_linear_entries(pg)
+#define LPT_ASSERT(x)
+#endif
 
 
 bool is_iomem_page(mfn_t mfn)
@@ -2260,7 +2290,7 @@ static int _put_final_page_type(struct page_info *page, 
unsigned long type,
             dec_linear_uses(page);
             dec_linear_entries(ptpg);
         }
-        ASSERT(!page->linear_pt_count || page_get_owner(page)->is_dying);
+        LPT_ASSERT(!page->linear_pt_count || page_get_owner(page)->is_dying);
         set_tlbflush_timestamp(page);
         smp_wmb();
         page->u.inuse.type_info--;
@@ -2330,8 +2360,8 @@ static int _put_page_type(struct page_info *page, bool 
preemptible,
                  * necessary anymore for a dying domain.
                  */
                 ASSERT(page_get_owner(page)->is_dying);
-                ASSERT(page->linear_pt_count < 0);
-                ASSERT(ptpg->linear_pt_count > 0);
+                LPT_ASSERT(page->linear_pt_count < 0);
+                LPT_ASSERT(ptpg->linear_pt_count > 0);
                 ptpg = NULL;
             }
 
@@ -2508,7 +2538,7 @@ static int __get_page_type(struct page_info *page, 
unsigned long type,
             page->nr_validated_ptes = 0;
             page->partial_pte = 0;
         }
-        page->linear_pt_count = 0;
+        zero_linear_entries(page);
         rc = alloc_page_type(page, type, preemptible);
     }
 
diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h
index 26f0153164..7825f36316 100644
--- a/xen/include/asm-x86/mm.h
+++ b/xen/include/asm-x86/mm.h
@@ -177,10 +177,15 @@ struct page_info
          *   in use.
          */
         struct {
+#ifdef CONFIG_PV_LINEAR_PT
             u16 nr_validated_ptes:PAGETABLE_ORDER + 1;
             u16 :16 - PAGETABLE_ORDER - 1 - 2;
             s16 partial_pte:2;
             s16 linear_pt_count;
+#else
+            u16 nr_validated_ptes;
+            s8 partial_pte;
+#endif
         };
 
         /*
-- 
2.14.2


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.