The gfn references need to remain held until after the p2m_set_entry() has
completed.  This is only a latent bug for now, because there is no per-gfn
locking and we recursively hold the main p2m locks.

Rearrange the code to have a single exit path, and defer taking the ap2m lock
until it is necessary to do so.  Leave some comments behind to help people
attempting to follow the logic.

Signed-off-by: Andrew Cooper <andrew.coop...@citrix.com>
---
CC: Jan Beulich <jbeul...@suse.com>
CC: Wei Liu <wei.l...@citrix.com>
CC: Roger Pau Monné <roger....@citrix.com>
CC: Razvan Cojocaru <rcojoc...@bitdefender.com>
CC: Tamas K Lengyel <ta...@tklengyel.com>
CC: George Dunlap <george.dun...@eu.citrix.com>
---
 xen/arch/x86/mm/p2m.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index b5a59d6..ae9cb20 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -2184,24 +2184,29 @@ bool_t p2m_altp2m_lazy_copy(struct vcpu *v, paddr_t gpa,
     unsigned long mask;
     mfn_t mfn;
     int rv;
+    bool ret;
 
     *ap2m = p2m_get_altp2m(v);
 
     mfn = get_gfn_type_access(*ap2m, gfn_x(gfn), &p2mt, &p2ma,
                               0, &page_order);
-    __put_gfn(*ap2m, gfn_x(gfn));
 
+    /* Entry already present in ap2m?  Caller should handle the fault. */
     if ( !mfn_eq(mfn, INVALID_MFN) )
-        return 0;
+    {
+        ret = false;
+        goto put_ap2m;
+    }
 
     mfn = get_gfn_type_access(hp2m, gfn_x(gfn), &p2mt, &p2ma,
                               P2M_ALLOC, &page_order);
-    __put_gfn(hp2m, gfn_x(gfn));
 
+    /* Entry not present in hp2m?  Caller should handle the fault. */
     if ( mfn_eq(mfn, INVALID_MFN) )
-        return 0;
-
-    p2m_lock(*ap2m);
+    {
+        ret = false;
+        goto put_hp2m;
+    }
 
     /*
      * If this is a superpage mapping, round down both frame numbers
@@ -2211,6 +2216,7 @@ bool_t p2m_altp2m_lazy_copy(struct vcpu *v, paddr_t gpa,
     mfn = _mfn(mfn_x(mfn) & mask);
     gfn = _gfn(gfn_x(gfn) & mask);
 
+    p2m_lock(*ap2m);
     rv = p2m_set_entry(*ap2m, gfn, mfn, page_order, p2mt, p2ma);
     p2m_unlock(*ap2m);
 
@@ -2222,7 +2228,14 @@ bool_t p2m_altp2m_lazy_copy(struct vcpu *v, paddr_t gpa,
         domain_crash(hp2m->domain);
     }
 
-    return 1;
+    ret = true;
+
+put_hp2m:
+    __put_gfn(hp2m, gfn_x(gfn));
+put_ap2m:
+    __put_gfn(*ap2m, gfn_x(gfn));
+
+    return ret;
 }
 
 void p2m_flush_altp2m(struct domain *d)
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel

Reply via email to