]> xenbits.xensource.com Git - xen.git/commitdiff
x86/shadow: Infrastructure to force a PV guest into shadow mode
authorJuergen Gross <jgross@suse.com>
Mon, 23 Jul 2018 06:11:40 +0000 (07:11 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 14 Aug 2018 15:56:47 +0000 (16:56 +0100)
To mitigate L1TF, we cannot alter an architecturally-legitimate PTE a PV guest
chooses to write, but we can force the PV domain into shadow mode so Xen
controls the PTEs which are reachable by the CPU pagewalk.

Introduce new shadow mode, PG_SH_forced, and a tasklet to perform the
transition.  Later patches will introduce the logic to enable this mode at the
appropriate time.

To simplify vcpu cleanup, make tasklet_kill() idempotent with respect to
tasklet_init(), which involves adding a helper to check for an uninitialised
list head.

This is part of XSA-273 / CVE-2018-3620.

Signed-off-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Tim Deegan <tim@xen.org>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/mm/paging.c
xen/arch/x86/mm/shadow/common.c
xen/arch/x86/pv/domain.c
xen/common/tasklet.c
xen/include/asm-x86/domain.h
xen/include/asm-x86/paging.h
xen/include/asm-x86/shadow.h
xen/include/xen/list.h

index 2b0445ffe915d1fded925e31b62224f4391e49ef..dcee496eb0873ebe4d21e166150357cff1da07d4 100644 (file)
@@ -873,6 +873,8 @@ void paging_dump_domain_info(struct domain *d)
         printk("    paging assistance: ");
         if ( paging_mode_shadow(d) )
             printk("shadow ");
+        if ( paging_mode_sh_forced(d) )
+            printk("forced ");
         if ( paging_mode_hap(d) )
             printk("hap ");
         if ( paging_mode_refcounts(d) )
index dd61b50eb76a2843cedb3f606676c4cef63f3e10..fd42d734e70afb33917b6295b1c6c13978c67a77 100644 (file)
@@ -3177,6 +3177,15 @@ static void sh_new_mode(struct domain *d, u32 new_mode)
     ASSERT(paging_locked_by_me(d));
     ASSERT(d != current->domain);
 
+    /*
+     * If PG_SH_forced has previously been activated because of writing an
+     * L1TF-vulnerable PTE, it must remain active for the remaining lifetime
+     * of the domain, even if the logdirty mode needs to be controlled for
+     * migration purposes.
+     */
+    if ( paging_mode_sh_forced(d) )
+        new_mode |= PG_SH_forced | PG_SH_enable;
+
     d->arch.paging.mode = new_mode;
     for_each_vcpu(d, v)
         sh_update_paging_modes(v);
@@ -4057,6 +4066,33 @@ void shadow_audit_tables(struct vcpu *v)
 
 #endif /* Shadow audit */
 
+#ifdef CONFIG_PV
+
+void pv_l1tf_tasklet(unsigned long data)
+{
+    struct domain *d = (void *)data;
+
+    domain_pause(d);
+    paging_lock(d);
+
+    if ( !paging_mode_sh_forced(d) && !d->is_dying )
+    {
+        int ret = shadow_one_bit_enable(d, PG_SH_forced);
+
+        if ( ret )
+        {
+            printk(XENLOG_G_ERR "d%d Failed to enable PG_SH_forced: %d\n",
+                   d->domain_id, ret);
+            domain_crash(d);
+        }
+    }
+
+    paging_unlock(d);
+    domain_unpause(d);
+}
+
+#endif /* CONFIG_PV */
+
 /*
  * Local variables:
  * mode: C
index a4f0bd239d50294c8752aa5aecaecb83657cfff6..3230ac6a22bfc45bd02d4773c4f57266bedfc1e5 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/invpcid.h>
 #include <asm/spec_ctrl.h>
 #include <asm/pv/domain.h>
+#include <asm/shadow.h>
 
 static __read_mostly enum {
     PCID_OFF,
@@ -209,6 +210,8 @@ int pv_vcpu_initialise(struct vcpu *v)
 
 void pv_domain_destroy(struct domain *d)
 {
+    pv_l1tf_domain_destroy(d);
+
     destroy_perdomain_mapping(d, GDT_LDT_VIRT_START,
                               GDT_LDT_MBYTES << (20 - PAGE_SHIFT));
 
@@ -229,6 +232,8 @@ int pv_domain_initialise(struct domain *d)
     };
     int rc = -ENOMEM;
 
+    pv_l1tf_domain_init(d);
+
     d->arch.pv_domain.gdt_ldt_l1tab =
         alloc_xenheap_pages(0, MEMF_node(domain_to_node(d)));
     if ( !d->arch.pv_domain.gdt_ldt_l1tab )
index 0f0a6f83652820ba3edc4ed2ba03c48d21af71a4..d4fea3151cde1ec5e00cdfc433d540fb47655a69 100644 (file)
@@ -156,6 +156,10 @@ void tasklet_kill(struct tasklet *t)
 
     spin_lock_irqsave(&tasklet_lock, flags);
 
+    /* Cope with uninitialised tasklets. */
+    if ( list_head_is_null(&t->list) )
+        goto unlock;
+
     if ( !list_empty(&t->list) )
     {
         BUG_ON(t->is_dead || t->is_running || (t->scheduled_on < 0));
@@ -172,6 +176,7 @@ void tasklet_kill(struct tasklet *t)
         spin_lock_irqsave(&tasklet_lock, flags);
     }
 
+ unlock:
     spin_unlock_irqrestore(&tasklet_lock, flags);
 }
 
index e2442f4e52b33ed3eb739351541650eef2d3c066..09f6b3d2f8d0dd7e49a5031641d1ffa936a2a5d0 100644 (file)
@@ -121,6 +121,11 @@ struct shadow_domain {
 
     /* Has this domain ever used HVMOP_pagetable_dying? */
     bool_t pagetable_dying_op;
+
+#ifdef CONFIG_PV
+    /* PV L1 Terminal Fault mitigation. */
+    struct tasklet pv_l1tf_tasklet;
+#endif /* CONFIG_PV */
 #endif
 };
 
@@ -257,6 +262,8 @@ struct pv_domain
     bool xpti;
     /* Use PCID feature? */
     bool pcid;
+    /* Mitigate L1TF with shadow/crashing? */
+    bool check_l1tf;
 
     /* map_domain_page() mapping cache. */
     struct mapcache_domain mapcache;
index f0085511c78448f78e33daec6dfd18fd67553b89..f440e3e53cca38846485e6e81ca556aa11d918d5 100644 (file)
 
 #define PG_SH_shift    20
 #define PG_HAP_shift   21
+#define PG_SHF_shift   22
 /* We're in one of the shadow modes */
 #ifdef CONFIG_SHADOW_PAGING
 #define PG_SH_enable   (1U << PG_SH_shift)
+#define PG_SH_forced   (1U << PG_SHF_shift)
 #else
 #define PG_SH_enable   0
+#define PG_SH_forced   0
 #endif
 #define PG_HAP_enable  (1U << PG_HAP_shift)
 
@@ -62,6 +65,7 @@
 
 #define paging_mode_enabled(_d)   (!!(_d)->arch.paging.mode)
 #define paging_mode_shadow(_d)    (!!((_d)->arch.paging.mode & PG_SH_enable))
+#define paging_mode_sh_forced(_d) (!!((_d)->arch.paging.mode & PG_SH_forced))
 #define paging_mode_hap(_d)       (!!((_d)->arch.paging.mode & PG_HAP_enable))
 
 #define paging_mode_refcounts(_d) (!!((_d)->arch.paging.mode & PG_refcounts))
index 94a34fd16a75c7dc46ed46ca197b6fc2a488ebae..14afb7db52c198b5fa73fb6f35821b7817641766 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/flushtlb.h>
 #include <asm/paging.h>
 #include <asm/p2m.h>
+#include <asm/spec_ctrl.h>
 
 /*****************************************************************************
  * Macros to tell which shadow paging mode a domain is in*/
@@ -115,6 +116,37 @@ static inline int shadow_domctl(struct domain *d,
 
 #endif /* CONFIG_SHADOW_PAGING */
 
+/*
+ * Mitigations for L1TF / CVE-2018-3620 for PV guests.
+ *
+ * We cannot alter an architecturally-legitimate PTE which a PV guest has
+ * chosen to write, as traditional paged-out metadata is L1TF-vulnerable.
+ * What we can do is force a PV guest which writes a vulnerable PTE into
+ * shadow mode, so Xen controls the pagetables which are reachable by the CPU
+ * pagewalk.
+ */
+
+void pv_l1tf_tasklet(unsigned long data);
+
+static inline void pv_l1tf_domain_init(struct domain *d)
+{
+    d->arch.pv_domain.check_l1tf =
+        opt_pv_l1tf & (is_hardware_domain(d)
+                       ? OPT_PV_L1TF_DOM0 : OPT_PV_L1TF_DOMU);
+
+#if defined(CONFIG_SHADOW_PAGING) && defined(CONFIG_PV)
+    tasklet_init(&d->arch.paging.shadow.pv_l1tf_tasklet,
+                 pv_l1tf_tasklet, (unsigned long)d);
+#endif
+}
+
+static inline void pv_l1tf_domain_destroy(struct domain *d)
+{
+#if defined(CONFIG_SHADOW_PAGING) && defined(CONFIG_PV)
+    tasklet_kill(&d->arch.paging.shadow.pv_l1tf_tasklet);
+#endif
+}
+
 /* Remove all shadows of the guest mfn. */
 static inline void shadow_remove_all_shadows(struct domain *d, mfn_t gmfn)
 {
index fa07d720eec5d2140d2834ea67205e4ed36d5c05..1387abb21192668f4103620520cf4256902a45aa 100644 (file)
@@ -51,6 +51,11 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
     list->prev = list;
 }
 
+static inline bool list_head_is_null(const struct list_head *list)
+{
+    return !list->next && !list->prev;
+}
+
 /*
  * Insert a new entry between two known consecutive entries. 
  *