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

[Xen-changelog] [xen stable-4.9] x86/mm: Properly handle linear pagetable promotion failures



commit b0147bd6e4ebe362adad64e4930145b3dce77ec4
Author:     George Dunlap <george.dunlap@xxxxxxxxxx>
AuthorDate: Mon Nov 4 15:07:12 2019 +0100
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Mon Nov 4 15:07:12 2019 +0100

    x86/mm: Properly handle linear pagetable promotion failures
    
    In order to allow recursive pagetable promotions and demotions to be
    interrupted, Xen must keep track of the state of the sub-pages
    promoted or demoted.  This is stored in two elements in the page
    struct: nr_entries_validated and partial_flags.
    
    The rule is that entries [0, nr_entries_validated) should always be
    validated and hold a general reference count.  If partial_flags is
    zero, then [nr_entries_validated] is not validated and no reference
    count is held.  If PTF_partial_set is set, then [nr_entries_validated]
    is partially validated, and a general reference count is held.
    
    Unfortunately, in cases where an entry began with PTF_partial_set set,
    and get_page_from_lNe() returns -EINVAL, the PTF_partial_set bit is
    erroneously dropped.  (This scenario can be engineered mainly by the
    use of interleaving of promoting and demoting a page which has "linear
    pagetable" entries; see the appendix for a sketch.)  This means that
    we will "leak" a general reference count on the page in question,
    preventing the page from being freed.
    
    Fix this by setting page->partial_flags to the partial_flags local
    variable.
    
    This is part of XSA-299.
    
    Reported-by: George Dunlap <george.dunlap@xxxxxxxxxx>
    Signed-off-by: George Dunlap <george.dunlap@xxxxxxxxxx>
    Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
    -----
    Appendix
    
    Suppose A and B can both be promoted to L2 pages, and A[x] points to B.
    
    V1: PIN_L2 B.
      B.type_count = 1 | PGT_validated
      B.count = 2 | PGC_allocated
    
    V1: MOD_L3_ENTRY pointing something to A.
      In the process of validating A[x], grab an extra type / ref on B:
      B.type_count = 2 | PGT_validated
      B.count = 3 | PGC_allocated
      A.type_count = 1 | PGT_validated
      A.count = 2 | PGC_allocated
    
    V1: UNPIN B.
      B.type_count = 1 | PGT_validate
      B.count = 2 | PGC_allocated
    
    V1: MOD_L3_ENTRY removing the reference to A.
      De-validate A, down to A[x], which points to B.
      Drop the final type on B.  Arrange to be interrupted.
      B.type_count = 1 | PGT_partial
      B.count = 2 | PGC_allocated
      A.type_count = 1 | PGT_partial
      A.nr_validated_entries = x
      A.partial_pte = -1
    
    V2: MOD_L3_ENTRY adds a reference to A.
    
    At this point, get_page_from_l2e(A[x]) tries
    get_page_and_type_from_mfn(), which fails because it's the wrong type;
    and get_l2_linear_pagetable() also fails, because B isn't validated as
    an l2 anymore.
    master commit: 2f126247ef49c2ba52bae29a2ab371059ede67c0
    master date: 2019-10-31 16:15:48 +0100
---
 xen/arch/x86/mm.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index ee91c69537..19b16de5d1 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -1752,7 +1752,7 @@ static int alloc_l2_table(struct page_info *page, 
unsigned long type)
             if ( i )
             {
                 page->nr_validated_ptes = i;
-                page->partial_flags = 0;
+                page->partial_flags = partial_flags;
                 current->arch.old_guest_ptpg = NULL;
                 current->arch.old_guest_table = page;
             }
@@ -1848,7 +1848,7 @@ static int alloc_l3_table(struct page_info *page)
         if ( i )
         {
             page->nr_validated_ptes = i;
-            page->partial_flags = 0;
+            page->partial_flags = partial_flags;
             current->arch.old_guest_ptpg = NULL;
             current->arch.old_guest_table = page;
         }
@@ -1939,7 +1939,7 @@ static int alloc_l4_table(struct page_info *page)
             if ( i )
             {
                 page->nr_validated_ptes = i;
-                page->partial_flags = 0;
+                page->partial_flags = partial_flags;
                 if ( rc == -EINTR )
                     rc = -ERESTART;
                 else
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.9

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/xen-changelog

 


Rackspace

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