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

[xen staging-4.12] x86/PoD: deal with misaligned GFNs



commit 398f7aec5dab6ad0853a8b069b48ae304e059fa4
Author:     Jan Beulich <jbeulich@xxxxxxxx>
AuthorDate: Tue Nov 23 13:35:36 2021 +0100
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Tue Nov 23 13:35:36 2021 +0100

    x86/PoD: deal with misaligned GFNs
    
    Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't
    required to pass in order-aligned GFN values. (While I consider this
    bogus, I don't think we can fix this there, as that might break existing
    code, e.g Linux'es swiotlb, which - while affecting PV only - until
    recently had been enforcing only page alignment on the original
    allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(),
    p2m_set_entry()) look to be dealing with this properly (in part by being
    implemented inefficiently, handling every 4k page separately).
    
    Introduce wrappers taking care of splitting the incoming request into
    aligned chunks, without putting much effort in trying to determine the
    largest possible chunk at every iteration.
    
    Also "handle" p2m_set_entry() failure for non-order-0 requests by
    crashing the domain in one more place. Alongside putting a log message
    there, also add one to the other similar path.
    
    Note regarding locking: This is left in the actual worker functions on
    the assumption that callers aren't guaranteed atomicity wrt acting on
    multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have
    locked the correct GFN range anyway, if it didn't simply resolve to
    p2m_lock(), and for well-behaved callers there continues to be only a
    single iteration, i.e. behavior is unchanged for them. (FTAOD pulling
    out just pod_lock() into p2m_pod_decrease_reservation() would result in
    a lock order violation.)
    
    This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388.
    
    Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order 
ranges")
    Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
    Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
    master commit: 182c737b9ba540ebceb1433f3940fbed6eac4ea9
    master date: 2021-11-22 12:27:30 +0000
---
 xen/arch/x86/mm/p2m-pod.c | 75 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 63 insertions(+), 12 deletions(-)

diff --git a/xen/arch/x86/mm/p2m-pod.c b/xen/arch/x86/mm/p2m-pod.c
index f4e5a35578..6e3d474b80 100644
--- a/xen/arch/x86/mm/p2m-pod.c
+++ b/xen/arch/x86/mm/p2m-pod.c
@@ -497,7 +497,7 @@ p2m_pod_zero_check_superpage(struct p2m_domain *p2m, gfn_t 
gfn);
 
 
 /*
- * This function is needed for two reasons:
+ * This pair of functions is needed for two reasons:
  * + To properly handle clearing of PoD entries
  * + To "steal back" memory being freed for the PoD cache, rather than
  *   releasing it.
@@ -505,8 +505,8 @@ p2m_pod_zero_check_superpage(struct p2m_domain *p2m, gfn_t 
gfn);
  * Once both of these functions have been completed, we can return and
  * allow decrease_reservation() to handle everything else.
  */
-unsigned long
-p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
+static unsigned long
+decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
 {
     unsigned long ret = 0, i, n;
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
@@ -559,8 +559,10 @@ p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, 
unsigned int order)
          * All PoD: Mark the whole region invalid and tell caller
          * we're done.
          */
-        if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid,
-                           p2m->default_access) )
+        int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid,
+                               p2m->default_access);
+
+        if ( rc )
         {
             /*
              * If this fails, we can't tell how much of the range was changed.
@@ -568,7 +570,12 @@ p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, 
unsigned int order)
              * impossible.
              */
             if ( order != 0 )
+            {
+                printk(XENLOG_G_ERR
+                       "%pd: marking GFN %#lx (order %u) as non-PoD failed: 
%d\n",
+                       d, gfn_x(gfn), order, rc);
                 domain_crash(d);
+            }
             goto out_unlock;
         }
         ret = 1UL << order;
@@ -676,6 +683,22 @@ out_unlock:
     return ret;
 }
 
+unsigned long
+p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order)
+{
+    unsigned long left = 1UL << order, ret = 0;
+    unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left);
+
+    do {
+        ret += decrease_reservation(d, gfn, chunk_order);
+
+        left -= 1UL << chunk_order;
+        gfn = gfn_add(gfn, 1UL << chunk_order);
+    } while ( left );
+
+    return ret;
+}
+
 void p2m_pod_dump_data(struct domain *d)
 {
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
@@ -1271,19 +1294,15 @@ remap_and_retry:
     return true;
 }
 
-
-int
-guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l,
-                                      unsigned int order)
+static int
+mark_populate_on_demand(struct domain *d, unsigned long gfn_l,
+                        unsigned int order)
 {
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
     gfn_t gfn = _gfn(gfn_l);
     unsigned long i, n, pod_count = 0;
     int rc = 0;
 
-    if ( !paging_mode_translate(d) )
-        return -EINVAL;
-
     gfn_lock(p2m, gfn, order);
 
     P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l);
@@ -1321,6 +1340,17 @@ guest_physmap_mark_populate_on_demand(struct domain *d, 
unsigned long gfn_l,
         BUG_ON(p2m->pod.entry_count < 0);
         pod_unlock(p2m);
     }
+    else if ( order )
+    {
+        /*
+         * If this failed, we can't tell how much of the range was changed.
+         * Best to crash the domain.
+         */
+        printk(XENLOG_G_ERR
+               "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n",
+               d, gfn_l, order, rc);
+        domain_crash(d);
+    }
 
 out:
     gfn_unlock(p2m, gfn, order);
@@ -1328,6 +1358,27 @@ out:
     return rc;
 }
 
+int
+guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn,
+                                      unsigned int order)
+{
+    unsigned long left = 1UL << order;
+    unsigned int chunk_order = find_first_set_bit(gfn | left);
+    int rc;
+
+    if ( !paging_mode_translate(d) )
+        return -EINVAL;
+
+    do {
+        rc = mark_populate_on_demand(d, gfn, chunk_order);
+
+        left -= 1UL << chunk_order;
+        gfn += 1UL << chunk_order;
+    } while ( !rc && left );
+
+    return rc;
+}
+
 void p2m_pod_init(struct p2m_domain *p2m)
 {
     unsigned int i;
--
generated by git-patchbot for /home/xen/git/xen.git#staging-4.12



 


Rackspace

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