x86: don't wrongly trigger linear page table assertion
authorJan Beulich <jbeulich@suse.com>
Thu, 16 Nov 2017 09:37:29 +0000 (10:37 +0100)
committerJan Beulich <jbeulich@suse.com>
Thu, 16 Nov 2017 09:37:29 +0000 (10:37 +0100)
_put_page_type() may do multiple iterations until its cmpxchg()
succeeds. It invokes set_tlbflush_timestamp() on the first
iteration, however. Code inside the function takes care of this, but
- the assertion in _put_final_page_type() would trigger on the second
  iteration if time stamps in a debug build are permitted to be
  sufficiently much wider than the default 6 bits (see WRAP_MASK in
  flushtlb.c),
- it returning -EINTR (for a continuation to be scheduled) would leave
  the page inconsistent state (until the re-invocation completes).
Make the set_tlbflush_timestamp() invocation conditional, bypassing it
(for now) only in the case we really can't tolerate the stamp to be
stored.

This is part of XSA-240.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: George Dunlap <george.dunlap@citrix.com>
xen/arch/x86/mm.c

index 64ccd70ce3e796f9d0e10bafc84d4ea87ea0a651..886a5ee327df8d378f64b3e6789de3443690b078 100644 (file)
@@ -2477,26 +2477,19 @@ static int _put_page_type(struct page_info *page, bool preemptible,
                 break;
             }
 
-#ifdef CONFIG_PV_LINEAR_PT
-            if ( ptpg && PGT_type_equal(x, ptpg->u.inuse.type_info) )
+            if ( !ptpg || !PGT_type_equal(x, ptpg->u.inuse.type_info) )
             {
                 /*
                  * set_tlbflush_timestamp() accesses the same union
-                 * linear_pt_count lives in. Unvalidated page table pages,
-                 * however, should occur during domain destruction only
-                 * anyway.  Updating of linear_pt_count luckily is not
-                 * necessary anymore for a dying domain.
+                 * linear_pt_count lives in. Pages (including page table ones),
+                 * however, don't need their flush time stamp set except when
+                 * the last reference is being dropped. For page table pages
+                 * this happens in _put_final_page_type().
                  */
-                ASSERT(page_get_owner(page)->is_dying);
-                ASSERT(page->linear_pt_count < 0);
-                ASSERT(ptpg->linear_pt_count > 0);
-                ptpg = NULL;
+                set_tlbflush_timestamp(page);
             }
-#else /* CONFIG_PV_LINEAR_PT */
-            BUG_ON(ptpg && PGT_type_equal(x, ptpg->u.inuse.type_info));
-#endif
-
-            set_tlbflush_timestamp(page);
+            else
+                BUG_ON(!IS_ENABLED(CONFIG_PV_LINEAR_PT));
         }
         else if ( unlikely((nx & (PGT_locked | PGT_count_mask)) ==
                            (PGT_locked | 1)) )