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

[xen master] xen/page_alloc: ensure TLB flush is done ahead of page scrubbing



commit ed7e656e6c18c1f5e6c7d0332fdaa8eeeee1b24e
Author:     Roger Pau Monne <roger@xxxxxxxxxxxxxx>
AuthorDate: Tue Aug 4 12:23:19 2026 +0200
Commit:     Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
CommitDate: Tue Sep 8 12:57:23 2026 +0100

    xen/page_alloc: ensure TLB flush is done ahead of page scrubbing
    
    The current way in which idle TLB flush and TLB flushing when allocating a
    page are done allows for the scrubbing to be done ahead of the TLB flush.
    A PV domain can still have a TLB entry for the page after scrubbing, and
    hence it may be able to modify it.  Such unintended page accessing allows
    domains to possibly exchange information even when `xsm=silo scrub-domheap`
    are in effect.
    
    Remove the MEMF_no_tlbflush memory allocation flag, and reorder the
    flushing so it's always done ahead of the scrubbing in
    alloc_{,color_}heap_pages().  The sole user of MEMF_no_tlbflush is
    populate_physmap(), and given the constrains above it's no longer safe
    to defer the flush, hence the flag removal and the folding of the flush in
    the allocator function itself.
    
    This is XSA-511 / CVE-2026-79603.
    
    Fixes: 24f1a58d1954 ("mm: option to _always_ scrub freed domheap pages")
    Signed-off-by: Roger Pau Monné <roger@xxxxxxxxxxxxxx>
    Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
---
 xen/common/memory.c     | 21 ---------------------
 xen/common/page_alloc.c | 34 +++++++++++++++++++---------------
 xen/include/xen/mm.h    |  2 --
 3 files changed, 19 insertions(+), 38 deletions(-)

diff --git a/xen/common/memory.c b/xen/common/memory.c
index 9443e35a7f..4afa2271b7 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -233,8 +233,6 @@ static void populate_physmap(struct memop_args *a)
     unsigned int i, j;
     xen_pfn_t gpfn;
     struct domain *d = a->domain, *curr_d = current->domain;
-    bool need_tlbflush = false;
-    uint32_t tlbflush_timestamp = 0;
 
     if ( !guest_handle_subrange_okay(a->extent_list, a->nr_done,
                                      a->nr_extents-1) )
@@ -246,15 +244,6 @@ static void populate_physmap(struct memop_args *a)
 
     if ( unlikely(!d->creation_finished) )
     {
-        /*
-         * With MEMF_no_tlbflush set, alloc_heap_pages() will ignore
-         * TLB-flushes. After VM creation, this is a security issue (it can
-         * make pages accessible to guest B, when guest A may still have a
-         * cached mapping to them). So we do this only during domain creation,
-         * when the domain itself has not yet been unpaused for the first
-         * time.
-         */
-        a->memflags |= MEMF_no_tlbflush;
         /*
          * With MEMF_no_icache_flush, alloc_heap_pages() will skip
          * performing icache flushes. We do it only before domain
@@ -397,13 +386,6 @@ static void populate_physmap(struct memop_args *a)
                     }
                 }
 
-                if ( unlikely(a->memflags & MEMF_no_tlbflush) )
-                {
-                    for ( j = 0; j < (1U << a->extent_order); j++ )
-                        accumulate_tlbflush(&need_tlbflush, &page[j],
-                                            &tlbflush_timestamp);
-                }
-
                 mfn = page_to_mfn(page);
             }
 
@@ -418,9 +400,6 @@ static void populate_physmap(struct memop_args *a)
     }
 
 out:
-    if ( need_tlbflush )
-        filtered_flush_tlb_mask(tlbflush_timestamp);
-
     if ( a->memflags & MEMF_no_icache_flush )
         invalidate_icache();
 
diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c
index 1e47f38721..62ac89b824 100644
--- a/xen/common/page_alloc.c
+++ b/xen/common/page_alloc.c
@@ -1101,15 +1101,17 @@ static struct page_info *alloc_heap_pages(
         /* Preserve PGC_need_scrub so we can check it after lock is dropped. */
         pg[i].count_info = PGC_state_inuse | (pg[i].count_info & 
PGC_need_scrub);
 
-        if ( !(memflags & MEMF_no_tlbflush) )
-            accumulate_tlbflush(&need_tlbflush, &pg[i],
-                                &tlbflush_timestamp);
+        accumulate_tlbflush(&need_tlbflush, &pg[i], &tlbflush_timestamp);
 
         init_free_page_fields(&pg[i]);
     }
 
     spin_unlock(&heap_lock);
 
+    /* Flush ahead of scrubbing: ensure no PV domain has a stale TLB entry. */
+    if ( need_tlbflush )
+        filtered_flush_tlb_mask(tlbflush_timestamp);
+
     if ( first_dirty != INVALID_DIRTY_IDX ||
          (scrub_debug && !(memflags & MEMF_no_scrub)) )
     {
@@ -1145,9 +1147,6 @@ static struct page_info *alloc_heap_pages(
         }
     }
 
-    if ( need_tlbflush )
-        filtered_flush_tlb_mask(tlbflush_timestamp);
-
     /*
      * Ensure cache and RAM are consistent for platforms where the guest
      * can control its own visibility of/through the cache.
@@ -1409,6 +1408,13 @@ bool scrub_free_pages(void)
                 {
                     if ( test_bit(_PGC_need_scrub, &pg[i].count_info) )
                     {
+                        bool need_tlbflush = false;
+                        uint32_t tlbflush_ts = 0;
+
+                        accumulate_tlbflush(&need_tlbflush, &pg[i], 
&tlbflush_ts);
+                        if ( need_tlbflush )
+                            filtered_flush_tlb_mask(tlbflush_ts);
+
                         scrub_one_page(&pg[i], true);
                         /*
                          * We can modify count_info without holding heap
@@ -2077,7 +2083,7 @@ static struct page_info *alloc_color_heap_page(unsigned 
int memflags,
     uint32_t tlbflush_timestamp = 0;
     bool need_scrub;
 
-    if ( memflags & ~(MEMF_no_refcount | MEMF_no_owner | MEMF_no_tlbflush |
+    if ( memflags & ~(MEMF_no_refcount | MEMF_no_owner |
                       MEMF_no_icache_flush | MEMF_no_scrub) )
         return NULL;
 
@@ -2106,13 +2112,16 @@ static struct page_info *alloc_color_heap_page(unsigned 
int memflags,
     free_colored_pages[color]--;
     page_list_del(pg, color_heap(color));
 
-    if ( !(memflags & MEMF_no_tlbflush) )
-        accumulate_tlbflush(&need_tlbflush, pg, &tlbflush_timestamp);
+    accumulate_tlbflush(&need_tlbflush, pg, &tlbflush_timestamp);
 
     init_free_page_fields(pg);
 
     spin_unlock(&heap_lock);
 
+    /* Flush ahead of scrubbing: ensure no PV domain has a stale TLB entry. */
+    if ( need_tlbflush )
+        filtered_flush_tlb_mask(tlbflush_timestamp);
+
     if ( !(memflags & MEMF_no_scrub) )
     {
         if ( need_scrub )
@@ -2121,9 +2130,6 @@ static struct page_info *alloc_color_heap_page(unsigned 
int memflags,
             check_one_page(pg);
     }
 
-    if ( need_tlbflush )
-        filtered_flush_tlb_mask(tlbflush_timestamp);
-
     flush_page_to_ram(mfn_x(page_to_mfn(pg)),
                       !(memflags & MEMF_no_icache_flush));
 
@@ -3041,9 +3047,7 @@ static bool prepare_staticmem_pages(struct page_info *pg, 
unsigned long nr_mfns,
             goto out_err;
         }
 
-        if ( !(memflags & MEMF_no_tlbflush) )
-            accumulate_tlbflush(&need_tlbflush, &pg[i],
-                                &tlbflush_timestamp);
+        accumulate_tlbflush(&need_tlbflush, &pg[i], &tlbflush_timestamp);
 
         /*
          * Preserve flag PGC_static and change page state
diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h
index fd8b0ba3f5..153c57f5dd 100644
--- a/xen/include/xen/mm.h
+++ b/xen/include/xen/mm.h
@@ -222,8 +222,6 @@ struct npfec {
 #define  MEMF_exact_node  (1U<<_MEMF_exact_node)
 #define _MEMF_no_owner    5
 #define  MEMF_no_owner    (1U<<_MEMF_no_owner)
-#define _MEMF_no_tlbflush 6
-#define  MEMF_no_tlbflush (1U<<_MEMF_no_tlbflush)
 #define _MEMF_no_icache_flush 7
 #define  MEMF_no_icache_flush (1U<<_MEMF_no_icache_flush)
 #define _MEMF_no_scrub    8
--
generated by git-patchbot for /home/xen/git/xen.git#master



 


Rackspace

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