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

[Xen-changelog] [xen stable-4.6] x86/shadow: hold references for the duration of emulated writes



commit 4d13019cb0471b6032a701c83081da63c17cad63
Author:     Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
AuthorDate: Tue Jun 20 16:39:18 2017 +0200
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Tue Jun 20 16:39:18 2017 +0200

    x86/shadow: hold references for the duration of emulated writes
    
    The (misnamed) emulate_gva_to_mfn() function translates a linear address to 
an
    mfn, but releases its page reference before returning the mfn to its caller.
    
    sh_emulate_map_dest() uses the results of one or two translations to 
construct
    a virtual mapping to the underlying frames, completes an emulated
    write/cmpxchg, then unmaps the virtual mappings.
    
    The page references need holding until the mappings are unmapped, or the
    frames can change ownership before the writes occurs.
    
    This is XSA-219.
    
    Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
    Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
    Reviewed-by: Tim Deegan <tim@xxxxxxx>
    master commit: 26217aff67ae1538d4e1b2226afab6993cdbe772
    master date: 2017-06-20 14:36:11 +0200
---
 xen/arch/x86/mm/shadow/multi.c | 56 +++++++++++++++++++++++++++++-------------
 1 file changed, 39 insertions(+), 17 deletions(-)

diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c
index 71477fe..c34ebe0 100644
--- a/xen/arch/x86/mm/shadow/multi.c
+++ b/xen/arch/x86/mm/shadow/multi.c
@@ -4586,7 +4586,10 @@ static void sh_pagetable_dying(struct vcpu *v, paddr_t 
gpa)
 /**************************************************************************/
 /* Handling HVM guest writes to pagetables  */
 
-/* Translate a VA to an MFN, injecting a page-fault if we fail */
+/*
+ * Translate a VA to an MFN, injecting a page-fault if we fail.  If the
+ * mapping succeeds, a reference will be held on the underlying page.
+ */
 #define BAD_GVA_TO_GFN (~0UL)
 #define BAD_GFN_TO_MFN (~1UL)
 #define READONLY_GFN   (~2UL)
@@ -4635,14 +4638,15 @@ static mfn_t emulate_gva_to_mfn(struct vcpu *v,
     ASSERT(mfn_valid(mfn));
 
     v->arch.paging.last_write_was_pt = !!sh_mfn_is_a_page_table(mfn);
-    /* Note shadow cannot page out or unshare this mfn, so the map won't
-     * disappear. Otherwise, caller must hold onto page until done. */
-    put_page(page);
+
     return mfn;
 }
 
-/* Check that the user is allowed to perform this write.
- * Returns a mapped pointer to write to, or NULL for error. */
+/*
+ * Check that the user is allowed to perform this write.  If a mapping is
+ * returned, page references will be held on sh_ctxt->mfn1 and
+ * sh_ctxt->mfn2 iff !INVALID_MFN.
+ */
 #define MAPPING_UNHANDLEABLE ((void *)(unsigned long)X86EMUL_UNHANDLEABLE)
 #define MAPPING_EXCEPTION    ((void *)(unsigned long)X86EMUL_EXCEPTION)
 #define MAPPING_SILENT_FAIL  ((void *)(unsigned long)X86EMUL_OKAY)
@@ -4655,13 +4659,6 @@ static void *emulate_map_dest(struct vcpu *v,
     struct domain *d = v->domain;
     void *map = NULL;
 
-    sh_ctxt->mfn1 = emulate_gva_to_mfn(v, vaddr, sh_ctxt);
-    if ( !mfn_valid(sh_ctxt->mfn1) )
-        return ((mfn_x(sh_ctxt->mfn1) == BAD_GVA_TO_GFN) ?
-                MAPPING_EXCEPTION :
-                (mfn_x(sh_ctxt->mfn1) == READONLY_GFN) ?
-                MAPPING_SILENT_FAIL : MAPPING_UNHANDLEABLE);
-
 #ifndef NDEBUG
     /* We don't emulate user-mode writes to page tables */
     if ( hvm_get_seg_reg(x86_seg_ss, sh_ctxt)->attr.fields.dpl == 3 )
@@ -4672,6 +4669,17 @@ static void *emulate_map_dest(struct vcpu *v,
     }
 #endif
 
+    sh_ctxt->mfn1 = emulate_gva_to_mfn(v, vaddr, sh_ctxt);
+    if ( !mfn_valid(sh_ctxt->mfn1) )
+    {
+        switch ( mfn_x(sh_ctxt->mfn1) )
+        {
+        case BAD_GVA_TO_GFN: return MAPPING_EXCEPTION;
+        case READONLY_GFN:   return MAPPING_SILENT_FAIL;
+        default:             return MAPPING_UNHANDLEABLE;
+        }
+    }
+
     /* Unaligned writes mean probably this isn't a pagetable */
     if ( vaddr & (bytes - 1) )
         sh_remove_shadows(d, sh_ctxt->mfn1, 0, 0 /* Slow, can fail */ );
@@ -4689,16 +4697,24 @@ static void *emulate_map_dest(struct vcpu *v,
         /* Cross-page emulated writes are only supported for HVM guests;
          * PV guests ought to know better */
         if ( !is_hvm_domain(d) )
+        {
+            put_page(mfn_to_page(sh_ctxt->mfn1));
             return MAPPING_UNHANDLEABLE;
+        }
 
         /* This write crosses a page boundary.  Translate the second page */
         sh_ctxt->mfn2 = emulate_gva_to_mfn(v, (vaddr + bytes - 1) & PAGE_MASK,
                                            sh_ctxt);
         if ( !mfn_valid(sh_ctxt->mfn2) )
-            return ((mfn_x(sh_ctxt->mfn2) == BAD_GVA_TO_GFN) ?
-                    MAPPING_EXCEPTION :
-                    (mfn_x(sh_ctxt->mfn2) == READONLY_GFN) ?
-                    MAPPING_SILENT_FAIL : MAPPING_UNHANDLEABLE);
+        {
+            put_page(mfn_to_page(sh_ctxt->mfn1));
+            switch ( mfn_x(sh_ctxt->mfn2) )
+            {
+            case BAD_GVA_TO_GFN: return MAPPING_EXCEPTION;
+            case READONLY_GFN:   return MAPPING_SILENT_FAIL;
+            default:             return MAPPING_UNHANDLEABLE;
+            }
+        }
 
         /* Cross-page writes mean probably not a pagetable */
         sh_remove_shadows(d, sh_ctxt->mfn2, 0, 0 /* Slow, can fail */ );
@@ -4707,7 +4723,11 @@ static void *emulate_map_dest(struct vcpu *v,
         mfns[1] = sh_ctxt->mfn2;
         map = vmap(mfns, 2);
         if ( !map )
+        {
+            put_page(mfn_to_page(sh_ctxt->mfn1));
+            put_page(mfn_to_page(sh_ctxt->mfn2));
             return MAPPING_UNHANDLEABLE;
+        }
         map += (vaddr & ~PAGE_MASK);
     }
 
@@ -4782,10 +4802,12 @@ static void emulate_unmap_dest(struct vcpu *v,
     }
 
     paging_mark_dirty(v->domain, mfn_x(sh_ctxt->mfn1));
+    put_page(mfn_to_page(sh_ctxt->mfn1));
 
     if ( unlikely(mfn_valid(sh_ctxt->mfn2)) )
     {
         paging_mark_dirty(v->domain, mfn_x(sh_ctxt->mfn2));
+        put_page(mfn_to_page(sh_ctxt->mfn2));
         vunmap((void *)((unsigned long)addr & PAGE_MASK));
     }
     else
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.6

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxx
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®.