Xen Security Advisory 89 - HVMOP_set_mem_access is not preemptible

Xen.org security team <[email protected]>
Newsgroups gmane.comp.emulators.xen.announce
Message-ID <E1WSQEn-0000fB-Ad__46461.5110926845$1395750141$gmane$org@xenbits.xen.org>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

                    Xen Security Advisory XSA-89
                             version 2

              HVMOP_set_mem_access is not preemptible

UPDATES IN VERSION 2
====================

Public release.

ISSUE DESCRIPTION
=================

Processing of the HVMOP_set_mem_access HVM control operations does not
check the size of its input and can tie up a physical CPU for extended
periods of time.

IMPACT
======

In a configuration where device models run with limited privilege (for
example, stubdom device models), a guest attacker who successfully
finds and exploits an unfixed security flaw in qemu-dm could leverage
the other flaw into a Denial of Service affecting the whole host.

In the more general case, in more abstract terms: a malicious
administrator of a domain privileged with regard to an HVM guest can
cause Xen to become unresponsive leading to a Denial of Service.

VULNERABLE SYSTEMS
==================

All Xen versions from 4.1 onwards are vulnerable. In 4.2 only 64-bit
versions of the hypervisor are vulnerable (HVMOP_set_mem_access is not
available in 32-bit hypervisors).

The vulnerability is only exposed to service domains for HVM guests
which have privilege over the guest.  In a usual configuration that
means only device model emulators (qemu-dm).

In the case of HVM guests whose device model is running in an
unrestricted dom0 process, qemu-dm already has the ability to cause
problems for the whole system.  So in that case the vulnerability is
not applicable.

The situation is more subtle for an HVM guest with a stub qemu-dm.
That is, where the device model runs in a separate domain (in the case
of xl, as requested by "device_model_stubdomain_override=1" in the xl
domain configuration file).  The same applies with a qemu-dm in a dom0
process subjected to some kind kernel-based process privilege
limitation (eg the chroot technique as found in some versions of
XCP/XenServer).

In those latter situations this issue means that the extra isolation
does not provide as good a defence (against denial of service) as
intended.  That is the essence of this vulnerability.

However, the security is still better than with a qemu-dm running as
an unrestricted dom0 process.  Therefore users with these
configurations should not switch to an unrestricted dom0 qemu-dm.

Finally, in a radically disaggregated system: where the HVM service
domain software (probably, the device model domain image) is not
always supplied by the host administrator, a malicious service domain
administrator can excercise this vulnerability.

MITIGATION
==========

Running only PV guests will avoid this vulnerability.

In a radically disaggregated system, restricting HVM service domains
to software images approved by the host administrator will avoid the
vulnerability.

CREDITS
=======

This issue was discovered by Jan Beulich.

RESOLUTION
==========

Applying the appropriate attached patch resolves this issue.

xsa89.patch        xen-unstable, Xen 4.4.x, Xen 4.3.x, Xen 4.2.x
xsa89-4.1.patch    Xen 4.1.x

$ sha256sum xsa89*.patch
741c8fbbfa8e425d8debba17135d4c2e1e962d15717769bc93d68a65b5dc5ea6  xsa89.patch
7d965e9bf1894b7d909bfaddbc6b7bdcee0ba91b86942ce85e0ae80464f2463e  xsa89-4.1.patch
$
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iQEcBAEBAgAGBQJTMXLgAAoJEIP+FMlX6CvZZ78H/RbnQJwEHxKxn3zhaEULpm57
zBPG1D2cGP12UCkFQLqR8tWvPYmEtm3/x/FQHjzTCBBCM3GMFJ9BiKOX+u5+h2Bu
17xPD3K8cH1tBkZpnQTkTBTz7XrfwV+C78kaNxo3TBvlgTIljaGCHxkXt0PmR1Vq
DPZEQdYXj/v8pblmyHYuhd6zf3n6V07ABLqHyPc9n6yZ4/o2LFjqQPZJpYFiFZI+
NGPw18+WCYlXc9w9ZtpGlNOo7Y5O2lraLLu7Gyi+JjC/BHXnb1XLgmgOSTyj2X5M
5v6zIMXy3vqaXHyjqw7uX6EzhCPfPhXAXVjpVGDin+RY/Ykp0QBDweUxZb4U71U=
=u+aG
-----END PGP SIGNATURE-----

_______________________________________________
Xen-announce mailing list
[email protected]
http://lists.xen.org/xen-announce
xsa89.patch (application/octet-stream, 2.9 KB)
x86: enforce preemption in HVM_set_mem_access / p2m_set_mem_access()

Processing up to 4G PFNs may take almost arbitrarily long, so
preemption is needed here.

This is XSA-89.

Signed-off-by: Jan Beulich <[email protected]>
Reviewed-by: Tim Deegan <[email protected]>

--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -4593,6 +4593,15 @@ long do_hvm_op(unsigned long op, XEN_GUE
             goto param_fail5;
             
         rc = p2m_set_mem_access(d, a.first_pfn, a.nr, a.hvmmem_access);
+        if ( rc > 0 )
+        {
+            a.first_pfn += a.nr - rc;
+            a.nr = rc;
+            if ( __copy_to_guest(arg, &a, 1) )
+                rc = -EFAULT;
+            else
+                rc = -EAGAIN;
+        }
 
     param_fail5:
         rcu_unlock_domain(d);
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1333,15 +1333,14 @@ void p2m_mem_access_resume(struct domain
 
 /* Set access type for a region of pfns.
  * If start_pfn == -1ul, sets the default access type */
-int p2m_set_mem_access(struct domain *d, unsigned long start_pfn, 
-                       uint32_t nr, hvmmem_access_t access) 
+long p2m_set_mem_access(struct domain *d, unsigned long pfn, uint32_t nr,
+                        hvmmem_access_t access)
 {
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
-    unsigned long pfn;
     p2m_access_t a, _a;
     p2m_type_t t;
     mfn_t mfn;
-    int rc = 0;
+    long rc;
 
     /* N.B. _not_ static: initializer depends on p2m->default_access */
     p2m_access_t memaccess[] = {
@@ -1364,14 +1363,17 @@ int p2m_set_mem_access(struct domain *d,
     a = memaccess[access];
 
     /* If request to set default access */
-    if ( start_pfn == ~0ull ) 
+    if ( pfn == ~0ul )
     {
         p2m->default_access = a;
         return 0;
     }
 
+    if ( !nr )
+        return 0;
+
     p2m_lock(p2m);
-    for ( pfn = start_pfn; pfn < start_pfn + nr; pfn++ )
+    for ( ; ; ++pfn )
     {
         mfn = p2m->get_entry(p2m, pfn, &t, &_a, 0, NULL);
         if ( p2m->set_entry(p2m, pfn, mfn, PAGE_ORDER_4K, t, a) == 0 )
@@ -1379,6 +1381,13 @@ int p2m_set_mem_access(struct domain *d,
             rc = -ENOMEM;
             break;
         }
+
+        /* Check for continuation if it's not the last interation. */
+        if ( !--nr || hypercall_preempt_check() )
+        {
+            rc = nr;
+            break;
+        }
     }
     p2m_unlock(p2m);
     return rc;
--- a/xen/include/asm-x86/p2m.h
+++ b/xen/include/asm-x86/p2m.h
@@ -576,8 +576,8 @@ void p2m_mem_access_resume(struct domain
 
 /* Set access type for a region of pfns.
  * If start_pfn == -1ul, sets the default access type */
-int p2m_set_mem_access(struct domain *d, unsigned long start_pfn, 
-                       uint32_t nr, hvmmem_access_t access);
+long p2m_set_mem_access(struct domain *d, unsigned long start_pfn,
+                        uint32_t nr, hvmmem_access_t access);
 
 /* Get access type for a pfn
  * If pfn == -1ul, gets the default access type */
xsa89-4.1.patch (application/octet-stream, 1.2 KB)
x86: enforce preemption in HVM_set_mem_access / p2m_set_mem_access()

Processing up to 4G PFNs may take almost arbitrarily long, so
preemption is needed here.

This is XSA-89.

Signed-off-by: Jan Beulich <[email protected]>
Reviewed-by: Tim Deegan <[email protected]>

--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -3755,7 +3755,7 @@ long do_hvm_op(unsigned long op, XEN_GUE
              ((a.first_pfn + a.nr - 1) > domain_get_maximum_gpfn(d)) )
             goto param_fail5;
             
-        for ( pfn = a.first_pfn; pfn < a.first_pfn + a.nr; pfn++ )
+        for ( pfn = a.first_pfn; a.nr; ++pfn )
         {
             p2m_type_t t;
             mfn_t mfn;
@@ -3768,6 +3768,17 @@ long do_hvm_op(unsigned long op, XEN_GUE
             p2m_unlock(p2m);
             if ( !success )
                 goto param_fail5;
+
+            /* Check for continuation if it's not the last interation. */
+            if ( --a.nr && hypercall_preempt_check() )
+            {
+                a.first_pfn = pfn + 1;
+                if ( copy_to_guest(arg, &a, 1) )
+                    rc = -EFAULT;
+                else
+                    rc = -EAGAIN;
+                goto param_fail5;
+            }
         }
 
         rc = 0;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.