xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
* Xen Security Advisory 128 (CVE-2015-4103) - Potential unintended writes to host MSI message data field via qemu
@ 2015-06-02 14:04 Xen.org security team
  0 siblings, 0 replies; only message in thread
From: Xen.org security team @ 2015-06-02 14:04 UTC (permalink / raw)
  To: xen-announce, xen-devel, xen-users, oss-security; +Cc: Xen.org security team

[-- Attachment #1: Type: text/plain, Size: 4112 bytes --]

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

            Xen Security Advisory CVE-2015-4103 / XSA-128
                              version 2

    Potential unintended writes to host MSI message data field via qemu

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

Public release.

CVE assigned.

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

Logic is in place to avoid writes to certain host config space fields
when the guest must nevertheless be able to access their virtual
counterparts.  A bug in how this logic deals with accesses spanning
multiple fields allows the guest to write to the host MSI message data
field.

While generally the writes write back the values previously read,
their value in config space may have got changed by the host between
the qemu read and write.  In such a case host side interrupt handling
could become confused, possibly losing interrupts or allowing spurious
interrupt injection into other guests.

IMPACT
======

Certain untrusted guest administrators may be able to confuse host
side interrupt handling, leading to a Denial of Service.

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

Xen versions 3.3 and onwards are vulnerable due to supporting PCI
pass-through.

Only x86 systems are vulnerable.  ARM systems are not vulnerable.

Only HVM guests with their device model run in Dom0 can take advantage
of this vulnerability.

Only HVM guests which have been granted access to physical PCI devices
(`PCI passthrough') can take advantage of this vulnerability.

Furthermore, the vulnerability is only applicable when the
passed-through PCI devices are MSI-capable.  (Most modern devices
are.)

MITIGATION
==========

This issue can be avoided by not assigning MSI capable PCI devices to
untrusted HVM guests.

This issue can also be avoided by only using PV guests.

It can also be avoided by configuring HVM guests with their device
model run in a separate (stub) domain.  (When using xl, this can be
requested with "device_model_stubdomain_override=1" in the domain
configuration file.)

CREDITS
=======

This issue was discovered by Jan Beulich of SUSE.

RESOLUTION
==========

Applying the appropriate attached patch resolves this issue.

xsa128-qemuu.patch           qemu-upstream-unstable, Xen 4.5.x, Xen 4.4.x
xsa128-qemuu-4.3.patch       Xen 4.3.x
xsa128-qemut.patch           qemu-xen-unstable, Xen 4.5.x, Xen 4.4.x, Xen 4.3.x, Xen 4.2.x

$ sha256sum xsa128*.patch
68b85a4c7d531d343d7fac2e92dbec3677bc2e4a83de75d78d7f605a2fc8ad3f  xsa128-qemut.patch
2ec657a6f22cac922854548c9d83698656ab7a36634ad05de7f14439cc4405bc  xsa128-qemuu-4.3.patch
104cf2e2816d253cc1eca3084f6ea9b6007f7773a88bda245bab00539e08b359  xsa128-qemuu.patch
$

DEPLOYMENT DURING EMBARGO
=========================

Deployment of the patches and/or mitigations described above (or
others which are substantially similar) is permitted during the
embargo, even on public-facing systems with untrusted guest users and
administrators.

But: Distribution of updated software is prohibited (except to other
members of the predisclosure list).

Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.

(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable.  This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)

For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
  http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iQEcBAEBAgAGBQJVbbdOAAoJEIP+FMlX6CvZEPUIAIti0HdxCX4JNy5MKqNFxHRB
KtGibssSaoGcPmkhLDqtOQ+8BwTUe/owezKlX799Jf0Jqn1bVXejCLyh0e6cyauq
pPoyQd+zblIpTFw3ByqVzicLajmVfY5v8yGGBAnSpuvfVEd3K5qWZCvFx+rEJ4AB
JI8jQdMAn2oFGtLbYDysRUpSjg/OtqIC6o3a4yfVnPDcduPq9XFpnxcdHHVfrklS
SeY1MGLbJtrNzya+zX1GZxFh5kuZnF/qSY3o60LF+2ZpK9nyH8toX1flvW9lXa86
9r1zxgy6qE1iWOHo4E1HjlK3lUUqW0XgkB/3zj+2LtX1uTwOhPtATn5/Neje0GY=
=4I3/
-----END PGP SIGNATURE-----

[-- Attachment #2: xsa128-qemut.patch --]
[-- Type: application/octet-stream, Size: 4766 bytes --]

xen: properly gate host writes of modified PCI CFG contents

The old logic didn't work as intended when an access spanned multiple
fields (for example a 32-bit access to the location of the MSI Message
Data field with the high 16 bits not being covered by any known field).
Remove it and derive which fields not to write to from the accessed
fields' emulation masks: When they're all ones, there's no point in
doing any host write.

This fixes a secondary issue at once: We obviously shouldn't make any
host write attempt when already the host read failed.

This is XSA-128.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

--- a/hw/pass-through.c
+++ b/hw/pass-through.c
@@ -454,7 +454,7 @@ static struct pt_reg_info_tbl pt_emu_reg
         .offset     = PCI_INTEL_OPREGION,
         .size       = 4,
         .init_val   = 0,
-        .no_wb      = 1,
+        .emu_mask   = 0xFFFFFFFF,
         .u.dw.read   = pt_intel_opregion_read,
         .u.dw.write  = pt_intel_opregion_write,
         .u.dw.restore  = NULL,
@@ -657,7 +657,6 @@ static struct pt_reg_info_tbl pt_emu_reg
         .init_val   = 0x00000000,
         .ro_mask    = 0x00000003,
         .emu_mask   = 0xFFFFFFFF,
-        .no_wb      = 1,
         .init       = pt_common_reg_init,
         .u.dw.read  = pt_long_reg_read,
         .u.dw.write = pt_msgaddr32_reg_write,
@@ -670,7 +669,6 @@ static struct pt_reg_info_tbl pt_emu_reg
         .init_val   = 0x00000000,
         .ro_mask    = 0x00000000,
         .emu_mask   = 0xFFFFFFFF,
-        .no_wb      = 1,
         .init       = pt_msgaddr64_reg_init,
         .u.dw.read  = pt_long_reg_read,
         .u.dw.write = pt_msgaddr64_reg_write,
@@ -683,7 +681,6 @@ static struct pt_reg_info_tbl pt_emu_reg
         .init_val   = 0x0000,
         .ro_mask    = 0x0000,
         .emu_mask   = 0xFFFF,
-        .no_wb      = 1,
         .init       = pt_msgdata_reg_init,
         .u.w.read   = pt_word_reg_read,
         .u.w.write  = pt_msgdata_reg_write,
@@ -696,7 +693,6 @@ static struct pt_reg_info_tbl pt_emu_reg
         .init_val   = 0x0000,
         .ro_mask    = 0x0000,
         .emu_mask   = 0xFFFF,
-        .no_wb      = 1,
         .init       = pt_msgdata_reg_init,
         .u.w.read   = pt_word_reg_read,
         .u.w.write  = pt_msgdata_reg_write,
@@ -1524,7 +1520,7 @@ static void pt_pci_write_config(PCIDevic
     uint32_t find_addr = address;
     uint32_t real_offset = 0;
     uint32_t valid_mask = 0xFFFFFFFF;
-    uint32_t read_val = 0;
+    uint32_t read_val = 0, wb_mask;
     uint8_t *ptr_val = NULL;
     int emul_len = 0;
     int index = 0;
@@ -1597,7 +1593,10 @@ static void pt_pci_write_config(PCIDevic
     {
         PT_LOG("Error: pci_read_block failed. return value[%d].\n", ret);
         memset((uint8_t *)&read_val, 0xff, len);
+        wb_mask = 0;
     }
+    else
+        wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
 
     /* pass directly to libpci for passthrough type register group */
     if (reg_grp_entry == NULL)
@@ -1620,6 +1619,11 @@ static void pt_pci_write_config(PCIDevic
             valid_mask = (0xFFFFFFFF >> ((4 - emul_len) << 3));
             valid_mask <<= ((find_addr - real_offset) << 3);
             ptr_val = ((uint8_t *)&val + (real_offset & 3));
+            if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
+                wb_mask &= ~((reg->emu_mask
+                              >> ((find_addr - real_offset) << 3))
+                             << ((len - emul_len) << 3));
+            }
 
             /* do emulation depend on register size */
             switch (reg->size) {
@@ -1677,8 +1681,19 @@ static void pt_pci_write_config(PCIDevic
     val >>= ((address & 3) << 3);
 
 out:
-    if (!(reg && reg->no_wb)) {  /* unknown regs are passed through */
-        ret = pci_write_block(pci_dev, address, (uint8_t *)&val, len);
+    for (index = 0; wb_mask; index += len) {
+        /* unknown regs are passed through */
+        while (!(wb_mask & 0xff)) {
+            index++;
+            wb_mask >>= 8;
+        }
+        len = 0;
+        do {
+            len++;
+            wb_mask >>= 8;
+        } while (wb_mask & 0xff);
+        ret = pci_write_block(pci_dev, address + index,
+                              (uint8_t *)&val + index, len);
 
         if (!ret)
             PT_LOG("Error: pci_write_block failed. return value[%d].\n", ret);
--- a/hw/pass-through.h
+++ b/hw/pass-through.h
@@ -372,8 +372,6 @@ struct pt_reg_info_tbl {
     uint32_t ro_mask;
     /* reg emulate field mask (ON:emu, OFF:passthrough) */
     uint32_t emu_mask;
-    /* no write back allowed */
-    uint32_t no_wb;
     /* emul reg initialize method */
     conf_reg_init init;
     union {

[-- Attachment #3: xsa128-qemuu-4.3.patch --]
[-- Type: application/octet-stream, Size: 4472 bytes --]

xen: properly gate host writes of modified PCI CFG contents

The old logic didn't work as intended when an access spanned multiple
fields (for example a 32-bit access to the location of the MSI Message
Data field with the high 16 bits not being covered by any known field).
Remove it and derive which fields not to write to from the accessed
fields' emulation masks: When they're all ones, there's no point in
doing any host write.

This fixes a secondary issue at once: We obviously shouldn't make any
host write attempt when already the host read failed.

This is XSA-128.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

--- a/hw/xen_pt.c
+++ b/hw/xen_pt.c
@@ -234,7 +234,7 @@ static void xen_pt_pci_write_config(PCID
     int index = 0;
     XenPTRegGroup *reg_grp_entry = NULL;
     int rc = 0;
-    uint32_t read_val = 0;
+    uint32_t read_val = 0, wb_mask;
     int emul_len = 0;
     XenPTReg *reg_entry = NULL;
     uint32_t find_addr = addr;
@@ -271,6 +271,9 @@ static void xen_pt_pci_write_config(PCID
     if (rc < 0) {
         XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
         memset(&read_val, 0xff, len);
+        wb_mask = 0;
+    } else {
+        wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
     }
 
     /* pass directly to the real device for passthrough type register group */
@@ -298,6 +301,11 @@ static void xen_pt_pci_write_config(PCID
 
             valid_mask <<= (find_addr - real_offset) << 3;
             ptr_val = (uint8_t *)&val + (real_offset & 3);
+            if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
+                wb_mask &= ~((reg->emu_mask
+                              >> ((find_addr - real_offset) << 3))
+                             << ((len - emul_len) << 3));
+            }
 
             /* do emulation based on register size */
             switch (reg->size) {
@@ -350,10 +358,19 @@ static void xen_pt_pci_write_config(PCID
     memory_region_transaction_commit();
 
 out:
-    if (!(reg && reg->no_wb)) {
+    for (index = 0; wb_mask; index += len) {
         /* unknown regs are passed through */
-        rc = xen_host_pci_set_block(&s->real_device, addr,
-                                    (uint8_t *)&val, len);
+        while (!(wb_mask & 0xff)) {
+            index++;
+            wb_mask >>= 8;
+        }
+        len = 0;
+        do {
+            len++;
+            wb_mask >>= 8;
+        } while (wb_mask & 0xff);
+        rc = xen_host_pci_set_block(&s->real_device, addr + index,
+                                    (uint8_t *)&val + index, len);
 
         if (rc < 0) {
             XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
--- a/hw/xen_pt.h
+++ b/hw/xen_pt.h
@@ -105,8 +105,6 @@ struct XenPTRegInfo {
     uint32_t ro_mask;
     /* reg emulate field mask (ON:emu, OFF:passthrough) */
     uint32_t emu_mask;
-    /* no write back allowed */
-    uint32_t no_wb;
     xen_pt_conf_reg_init init;
     /* read/write function pointer
      * for double_word/word/byte size */
--- a/hw/xen_pt_config_init.c
+++ b/hw/xen_pt_config_init.c
@@ -1281,7 +1281,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x00000000,
         .ro_mask    = 0x00000003,
         .emu_mask   = 0xFFFFFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_common_reg_init,
         .u.dw.read  = xen_pt_long_reg_read,
         .u.dw.write = xen_pt_msgaddr32_reg_write,
@@ -1293,7 +1292,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x00000000,
         .ro_mask    = 0x00000000,
         .emu_mask   = 0xFFFFFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_msgaddr64_reg_init,
         .u.dw.read  = xen_pt_long_reg_read,
         .u.dw.write = xen_pt_msgaddr64_reg_write,
@@ -1305,7 +1303,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x0000,
         .ro_mask    = 0x0000,
         .emu_mask   = 0xFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_msgdata_reg_init,
         .u.w.read   = xen_pt_word_reg_read,
         .u.w.write  = xen_pt_msgdata_reg_write,
@@ -1317,7 +1314,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x0000,
         .ro_mask    = 0x0000,
         .emu_mask   = 0xFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_msgdata_reg_init,
         .u.w.read   = xen_pt_word_reg_read,
         .u.w.write  = xen_pt_msgdata_reg_write,

[-- Attachment #4: xsa128-qemuu.patch --]
[-- Type: application/octet-stream, Size: 4496 bytes --]

xen: properly gate host writes of modified PCI CFG contents

The old logic didn't work as intended when an access spanned multiple
fields (for example a 32-bit access to the location of the MSI Message
Data field with the high 16 bits not being covered by any known field).
Remove it and derive which fields not to write to from the accessed
fields' emulation masks: When they're all ones, there's no point in
doing any host write.

This fixes a secondary issue at once: We obviously shouldn't make any
host write attempt when already the host read failed.

This is XSA-128.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -234,7 +234,7 @@ static void xen_pt_pci_write_config(PCID
     int index = 0;
     XenPTRegGroup *reg_grp_entry = NULL;
     int rc = 0;
-    uint32_t read_val = 0;
+    uint32_t read_val = 0, wb_mask;
     int emul_len = 0;
     XenPTReg *reg_entry = NULL;
     uint32_t find_addr = addr;
@@ -271,6 +271,9 @@ static void xen_pt_pci_write_config(PCID
     if (rc < 0) {
         XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
         memset(&read_val, 0xff, len);
+        wb_mask = 0;
+    } else {
+        wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
     }
 
     /* pass directly to the real device for passthrough type register group */
@@ -298,6 +301,11 @@ static void xen_pt_pci_write_config(PCID
 
             valid_mask <<= (find_addr - real_offset) << 3;
             ptr_val = (uint8_t *)&val + (real_offset & 3);
+            if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
+                wb_mask &= ~((reg->emu_mask
+                              >> ((find_addr - real_offset) << 3))
+                             << ((len - emul_len) << 3));
+            }
 
             /* do emulation based on register size */
             switch (reg->size) {
@@ -350,10 +358,19 @@ static void xen_pt_pci_write_config(PCID
     memory_region_transaction_commit();
 
 out:
-    if (!(reg && reg->no_wb)) {
+    for (index = 0; wb_mask; index += len) {
         /* unknown regs are passed through */
-        rc = xen_host_pci_set_block(&s->real_device, addr,
-                                    (uint8_t *)&val, len);
+        while (!(wb_mask & 0xff)) {
+            index++;
+            wb_mask >>= 8;
+        }
+        len = 0;
+        do {
+            len++;
+            wb_mask >>= 8;
+        } while (wb_mask & 0xff);
+        rc = xen_host_pci_set_block(&s->real_device, addr + index,
+                                    (uint8_t *)&val + index, len);
 
         if (rc < 0) {
             XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -105,8 +105,6 @@ struct XenPTRegInfo {
     uint32_t ro_mask;
     /* reg emulate field mask (ON:emu, OFF:passthrough) */
     uint32_t emu_mask;
-    /* no write back allowed */
-    uint32_t no_wb;
     xen_pt_conf_reg_init init;
     /* read/write function pointer
      * for double_word/word/byte size */
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -1281,7 +1281,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x00000000,
         .ro_mask    = 0x00000003,
         .emu_mask   = 0xFFFFFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_common_reg_init,
         .u.dw.read  = xen_pt_long_reg_read,
         .u.dw.write = xen_pt_msgaddr32_reg_write,
@@ -1293,7 +1292,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x00000000,
         .ro_mask    = 0x00000000,
         .emu_mask   = 0xFFFFFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_msgaddr64_reg_init,
         .u.dw.read  = xen_pt_long_reg_read,
         .u.dw.write = xen_pt_msgaddr64_reg_write,
@@ -1305,7 +1303,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x0000,
         .ro_mask    = 0x0000,
         .emu_mask   = 0xFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_msgdata_reg_init,
         .u.w.read   = xen_pt_word_reg_read,
         .u.w.write  = xen_pt_msgdata_reg_write,
@@ -1317,7 +1314,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[]
         .init_val   = 0x0000,
         .ro_mask    = 0x0000,
         .emu_mask   = 0xFFFF,
-        .no_wb      = 1,
         .init       = xen_pt_msgdata_reg_init,
         .u.w.read   = xen_pt_word_reg_read,
         .u.w.write  = xen_pt_msgdata_reg_write,

[-- Attachment #5: Type: text/plain, Size: 126 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2015-06-02 14:04 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-06-02 14:04 Xen Security Advisory 128 (CVE-2015-4103) - Potential unintended writes to host MSI message data field via qemu Xen.org security team

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).