File xsa285.patch of Package xen.11298

From: Jan Beulich <jbeulich@suse.com>
Subject: IOMMU/x86: fix type ref-counting race upon IOMMU page table construction

When arch_iommu_populate_page_table() gets invoked for an already
running guest, simply looking at page types once isn't enough, as they
may change at any time. Add logic to re-check the type after having
mapped the page, unmapping it again if needed.

This is XSA-285.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Tentatively-Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>

--- a/xen/drivers/passthrough/x86/iommu.c
+++ b/xen/drivers/passthrough/x86/iommu.c
@@ -59,10 +59,31 @@ int arch_iommu_populate_page_table(struc
         if ( is_hvm_domain(d) ||
             (page->u.inuse.type_info & PGT_type_mask) == PGT_writable_page )
         {
-            BUG_ON(SHARED_M2P(mfn_to_gmfn(d, page_to_mfn(page))));
-            rc = hd->platform_ops->map_page(
-                d, mfn_to_gmfn(d, page_to_mfn(page)), page_to_mfn(page),
-                IOMMUF_readable|IOMMUF_writable);
+            unsigned long gfn = mfn_to_gmfn(d, page_to_mfn(page));
+
+            BUG_ON(SHARED_M2P(gfn));
+            rc = hd->platform_ops->map_page(d, gfn, page_to_mfn(page),
+                                            IOMMUF_readable|IOMMUF_writable);
+
+            /*
+             * We may be working behind the back of a running guest, which may
+             * change the type of a page at any time.  We can't prevent this
+             * (for instance, by bumping the type count while mapping the page)
+             * without causing legitimate guest type-change operations to fail.
+             * So after adding the page to the IOMMU, check again to make sure
+             * this is still valid.  NB that the writable entry in the iommu is
+             * harmless until later, when the actual device gets assigned.
+             */
+            if ( !rc && !is_hvm_domain(d) &&
+                 ((page->u.inuse.type_info & PGT_type_mask) !=
+                  PGT_writable_page) )
+            {
+                rc = hd->platform_ops->unmap_page(d, gfn);
+                /* If the type changed yet again, simply force a retry. */
+                if ( !rc && ((page->u.inuse.type_info & PGT_type_mask) ==
+                             PGT_writable_page) )
+                    rc = -ERESTART;
+            }
             if ( rc )
             {
                 page_list_add(page, &d->page_list);
openSUSE Build Service is sponsored by