]> xenbits.xensource.com Git - xen.git/commitdiff
xen/page_alloc: Harden assign_pages()
authorJulien Grall <jgrall@amazon.com>
Mon, 22 Nov 2021 11:11:05 +0000 (11:11 +0000)
committerIan Jackson <iwj@xenproject.org>
Mon, 22 Nov 2021 11:11:05 +0000 (11:11 +0000)
domain_tot_pages() and d->max_pages are 32-bit values. While the order
should always be quite small, it would still be possible to overflow
if domain_tot_pages() is near to (2^32 - 1).

As this code may be called by a guest via XENMEM_increase_reservation
and XENMEM_populate_physmap, we want to make sure the guest is not going
to be able to allocate more than it is allowed.

Rework the allocation check to avoid any possible overflow. While the
check domain_tot_pages() < d->max_pages should technically not be
necessary, it is probably best to have it to catch any possible
inconsistencies in the future.

This is CVE-2021-28706 / part of XSA-385.

Signed-off-by: Julien Grall <jgrall@amazon.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
xen/common/grant_table.c
xen/common/page_alloc.c

index 8b322b51c07a447bd2aadb03e633b06c9116d495..012a74455ba68b54647591fafa96915d84fc3ef7 100644 (file)
@@ -2341,7 +2341,8 @@ gnttab_transfer(
          * pages when it is dying.
          */
         if ( unlikely(e->is_dying) ||
-             unlikely(domain_tot_pages(e) >= e->max_pages) )
+             unlikely(domain_tot_pages(e) >= e->max_pages) ||
+             unlikely(!(e->tot_pages + 1)) )
         {
             spin_unlock(&e->page_alloc_lock);
 
@@ -2350,8 +2351,8 @@ gnttab_transfer(
                          e->domain_id);
             else
                 gdprintk(XENLOG_INFO,
-                         "Transferee d%d has no headroom (tot %u, max %u)\n",
-                         e->domain_id, domain_tot_pages(e), e->max_pages);
+                         "Transferee %pd has no headroom (tot %u, max %u, ex %u)\n",
+                         e, domain_tot_pages(e), e->max_pages, e->extra_pages);
 
             gop.status = GNTST_general_error;
             goto unlock_and_copyback;
index 5801358b4ba0fdde4494d9e1ec6eaabc25ad7211..d0baaa2ecd2098892064a5f4490215071a6a8de4 100644 (file)
@@ -2310,20 +2310,41 @@ int assign_pages(
     }
     else if ( !(memflags & MEMF_no_refcount) )
     {
-        unsigned int tot_pages = domain_tot_pages(d) + nr;
+        unsigned int tot_pages = domain_tot_pages(d);
 
         if ( unlikely(tot_pages > d->max_pages) )
         {
-            gprintk(XENLOG_INFO, "Over-allocation for domain %u: "
-                    "%u > %u\n", d->domain_id, tot_pages, d->max_pages);
+            gprintk(XENLOG_INFO, "Inconsistent allocation for %pd: %u > %u\n",
+                    d, tot_pages, d->max_pages);
+            rc = -EPERM;
+            goto out;
+        }
+
+        if ( unlikely(nr > d->max_pages - tot_pages) )
+        {
+            gprintk(XENLOG_INFO, "Over-allocation for %pd: %Lu > %u\n",
+                    d, tot_pages + 0ull + nr, d->max_pages);
             rc = -E2BIG;
             goto out;
         }
     }
 
-    if ( !(memflags & MEMF_no_refcount) &&
-         unlikely(domain_adjust_tot_pages(d, nr) == nr) )
-        get_knownalive_domain(d);
+    if ( !(memflags & MEMF_no_refcount) )
+    {
+        if ( unlikely(d->tot_pages + nr < nr) )
+        {
+            gprintk(XENLOG_INFO,
+                    "Excess allocation for %pd: %Lu (%u extra)\n",
+                    d, d->tot_pages + 0ull + nr, d->extra_pages);
+            if ( pg[0].count_info & PGC_extra )
+                d->extra_pages -= nr;
+            rc = -E2BIG;
+            goto out;
+        }
+
+        if ( unlikely(domain_adjust_tot_pages(d, nr) == nr) )
+            get_knownalive_domain(d);
+    }
 
     for ( i = 0; i < nr; i++ )
     {