Compare commits

..

18 Commits
master ... f20

Author SHA1 Message Date
Cole Robinson a48950cf39 Work around temporary F20 mock breakage with BR /usr/bin/vi 2015-05-13 19:22:57 -04:00
Cole Robinson a1c144d276 CVE-2015-3456: (VENOM) fdc: out-of-bounds fifo buffer memory access (bz #1221152) 2015-05-13 18:17:40 -04:00
Cole Robinson c700dd0ffc Fix qemu_bh_schedule race condition (bz #1165315)
CVE-2014-8106: cirrus: insufficient blit region checks
2015-02-07 21:12:53 -05:00
Cole Robinson f4055dd206 Fix qemu-img convert corruption for unflushed files (bz #1167249) 2014-12-09 08:29:50 -05:00
Cole Robinson 7925752085 Fix SLES11 migration issue (bz #1109427)
CVE-2014-7840: insufficient parameter validation during ram load (bz #1163080)
2014-11-30 19:28:03 -05:00
Cole Robinson 56715e285b Fix SLES11 migration issue (bz #1109427)
CVE-2014-7840: insufficient parameter validation during ram load (bz #1163080)
2014-11-30 18:12:22 -05:00
Cole Robinson 1369de9828 CVE-2014-7815 vnc: insufficient bits_per_pixel from the client sanitization (bz #1157647, bz #1157641)
CVE-2014-3689 vmware_vga: insufficient parameter validation in rectangle functions (bz #1153038, bz #1153035)
2014-10-29 15:12:00 -04:00
Cole Robinson 87dd0b268a CVE-2014-3640 qemu: slirp: NULL pointer (bz #1144821, bz #1144818) 2014-09-26 12:02:24 -04:00
Cole Robinson d9094d4c0b Fix spice audio playback sample rate (bz #1129961)
Fix virtio-blk migration incompatibility caused by CVE backport (bz #1115604)
CVE-2014-3615 Fix crash when guest sets high resolution (bz #1139121, bz #1139115)
2014-09-08 10:45:38 -04:00
Rex Dieter f182f276c2 rebuild (for pulseaudio, bug #1117683) 2014-07-17 08:48:07 -05:00
Cole Robinson f602c82d48 QCOW1 validation CVEs: CVE-2014-0222, CVE-2014-0223 (bz #1097232, bz #1097238, bz #1097222, bz #1097216)
CVE-2014-3461: Issues in USB post load checks (bz #1097260, bz #1096821)
2014-05-31 21:01:22 -04:00
Cole Robinson e976b467c3 Migration CVEs: CVE-2014-0182 etc. 2014-05-11 18:43:54 -04:00
Cole Robinson 7452f5d212 Fix arm sd warnings with latest kernel (bz #1091548)
Fix regression in CVE backport that affects openstack (thanks lbezdick)
2014-04-29 14:09:00 -04:00
Cole Robinson 0d3567f1ae Fix guest startup crashes from autotest (bz #1081610) 2014-04-25 10:59:14 -04:00
Cole Robinson f3a92caa76 Block/image format validation CVE-2014-0142 - 2014-0148 (bz #1078201, bz #1086710, bz #1079140, bz #1086724, bz #1079240, bz #1086735, bz #1078885, bz #1086720, bz #1078232, bz #1086713, bz #1078848, bz #1086717, bz #1078212, bz #1086712)
CVE-2014-0150: virtio-net: buffer overflow in virtio_net_handle_mac() function (bz #1086775, bz #1078846)
CVE-2013-4544: vmxnet3: bounds checking buffer overrun (bz #1087513, bz #1087522)
CVE-2014-2894: out of bounds buffer accesses, guest triggerable via IDE SMART (bz #1087981, bz #1087971)
2014-04-24 17:36:31 -04:00
Cole Robinson 2e55f16ca5 Add patches 2014-03-19 15:43:47 -04:00
Cole Robinson 05010a02f4 Fix bogus libcacard dep on gluster (bz #987441)
Fix mouse with -display gtk -vga qxl (bz #1051724)
Change gtk quit accelerator to ctrl+shift+q (bz #1062393)
Fix crash during virtio-scsi hotplug (bz #1051611)
2014-03-19 15:36:16 -04:00
Cole Robinson 3fa1863e91 Add kill() to seccomp whitelist, fix AC97 with -sandbox on (bz #1043521)
Changing streaming mode default to off for spice (bz #1038336)
Fix guest scsi verify command (bz #1001617)
Fix performance regression after save/restore (bz #917723)
2013-12-18 12:14:24 -05:00
164 changed files with 14006 additions and 1807 deletions

31
.gitignore vendored
View File

@ -1,4 +1,27 @@
/.build*.log
/x86_64/
/*.src.rpm
/qemu-*.tar.xz
qemu-kvm-0.13.0-b81fe95.tar.gz
qemu-kvm-0.13.0-25fdf4a.tar.gz
/qemu-kvm-0.13.0-rc1.tar.gz
/qemu-kvm-0.13.0.tar.gz
/qemu-kvm-0.14.0-3593e6b.tar.gz
/qemu-kvm-0.14.0-7aa8c46.tar.gz
/qemu-kvm-0.15.0-525e3df.tar.gz
/qemu-kvm-0.15.0-fda1906.tar.gz
/qemu-kvm-0.15.0-59fadcc.tar.gz
/qemu-kvm-0.15.0-0af4922.tar.gz
/qemu-kvm-0.15.0.tar.gz
/qemu-kvm-0.15.1.tar.gz
/qemu-kvm-1.1.0.tar.gz
/qemu-kvm-1.1.1.tar.gz
/qemu-1.2-0.1.20120806git3e430569.fc18.src.rpm
/qemu-kvm-1.2-3e430569.tar.gz
/qemu-kvm-1.2.0-rc1.tar.gz
/qemu-kvm-1.2.0.tar.gz
/qemu-1.3.0.tar.bz2
/qemu-1.4.0.tar.bz2
/qemu-1.4.1.tar.bz2
/qemu-1.5.0.tar.bz2
/qemu-1.5.1.tar.bz2
/qemu-1.5.2.tar.bz2
/qemu-1.6.0.tar.bz2
/qemu-1.6.1.tar.bz2
/qemu-1.6.2.tar.bz2

View File

@ -0,0 +1,211 @@
From: Cole Robinson <crobinso@redhat.com>
Date: Fri, 16 Aug 2013 12:14:51 -0400
Subject: [PATCH] Fix migration from qemu-kvm
Details are in the code comments for each change. Just lumped this together
to ease patch maintenance.
Everything except the video memory bits can likely be dropped by Fedora 21
time frame. Need to figure out if there's anything to upstream for the
video memory bits.
---
hw/acpi/piix4.c | 8 ++++++-
hw/display/qxl.c | 9 ++++----
hw/i386/pc_piix.c | 61 +++++++++++++++++++++++++++++++++++++++++++++----
hw/timer/i8254_common.c | 7 +++++-
4 files changed, 74 insertions(+), 11 deletions(-)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 3aaf18c..6fbe57c 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -289,7 +289,13 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
static const VMStateDescription vmstate_acpi = {
.name = "piix4_pm",
.version_id = 3,
- .minimum_version_id = 3,
+ /*
+ * qemu-kvm 1.2 uses qemu.git version 3 format, but advertised as 2.
+ * This allows incoming migration from qemu-kvm, but breaks incoming
+ * migration from qemu < 1.3.
+ */
+ //minimum_version_id = 3,
+ .minimum_version_id = 2,
.minimum_version_id_old = 1,
.load_state_old = acpi_load_old,
.post_load = vmstate_acpi_post_load,
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index c537057..7ef3eff 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -307,16 +307,14 @@ static inline uint32_t msb_mask(uint32_t val)
return mask;
}
-static ram_addr_t qxl_rom_size(void)
+static void check_qxl_rom_size(PCIQXLDevice *d)
{
uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) +
sizeof(qxl_modes);
- uint32_t rom_size = 8192; /* two pages */
required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE);
required_rom_size = msb_mask(required_rom_size * 2 - 1);
- assert(required_rom_size <= rom_size);
- return rom_size;
+ assert(required_rom_size <= d->rom_size);
}
static void init_qxl_rom(PCIQXLDevice *d)
@@ -1981,7 +1979,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
- qxl->rom_size = qxl_rom_size();
+ check_qxl_rom_size(qxl);
memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom",
qxl->rom_size);
vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
@@ -2309,6 +2307,7 @@ static Property qxl_properties[] = {
DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
+ DEFINE_PROP_UINT32("rom_size", PCIQXLDevice, rom_size, 8192),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 3df2ff9..28216ee 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -377,6 +377,24 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
DEFAULT_MACHINE_OPTIONS,
};
+/*
+ * Commit 038c1879a00153b14bce113315b693e8c2944fa9 changed the qxl rom
+ * size to 8192, which fixes incoming migration from qemu 1.0. However
+ * from qemu 1.2 and 1.3 had rom size 16384, so incoming migration
+ * from those versions is now broken.
+ *
+ * Add a rom_size compat property. 1.2 and 1.3 get 16384, everything
+ * else is 8192.
+ *
+ * This isn't actually fool proof, since rom_size can be dependent on
+ * the version of spice qemu is built against:
+ *
+ * https://lists.gnu.org/archive/html/qemu-devel/2013-02/msg03154.html
+ *
+ * However these sizes match what native Fedora packages get, so it's
+ * good enough for now.
+ */
+
#define PC_COMPAT_1_3 \
PC_COMPAT_1_4, \
{\
@@ -395,8 +413,17 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
.driver = "e1000",\
.property = "autonegotiation",\
.value = "off",\
+ },{ \
+ .driver = "qxl", \
+ .property = "rom_size", \
+ .value = stringify(16384), \
+ },{\
+ .driver = "qxl-vga", \
+ .property = "rom_size", \
+ .value = stringify(16384), \
}
+
static QEMUMachine pc_machine_v1_3 = {
.name = "pc-1.3",
.desc = "Standard PC",
@@ -409,6 +436,19 @@ static QEMUMachine pc_machine_v1_3 = {
DEFAULT_MACHINE_OPTIONS,
};
+
+/*
+ * https://lists.gnu.org/archive/html/qemu-devel/2013-01/msg02540.html
+ *
+ * qemu-kvm defaulted to vgamem=16MB since at least 0.15, while qemu used
+ * 8MB. For qemu 1.2, the default was changed to 16MB for all devices
+ * except cirrus.
+ *
+ * Make sure cirrus uses 16MB for <= pc-1.2 (the qemu-kvm merge),
+ * and 16MB always for all others. This will break incoming qemu
+ * migration for qemu < 1.3.
+ */
+
#define PC_COMPAT_1_2 \
PC_COMPAT_1_3,\
{\
@@ -432,6 +472,10 @@ static QEMUMachine pc_machine_v1_3 = {
.property = "revision",\
.value = stringify(3),\
},{\
+ .driver = "cirrus-vga",\
+ .property = "vgamem_mb",\
+ .value = stringify(16),\
+ },{\
.driver = "VGA",\
.property = "mmio",\
.value = "off",\
@@ -462,25 +506,34 @@ static QEMUMachine pc_machine_v1_2 = {
},{\
.driver = "VGA",\
.property = "vgamem_mb",\
- .value = stringify(8),\
+ .value = stringify(16),\
},{\
.driver = "vmware-svga",\
.property = "vgamem_mb",\
- .value = stringify(8),\
+ .value = stringify(16),\
},{\
.driver = "qxl-vga",\
.property = "vgamem_mb",\
- .value = stringify(8),\
+ .value = stringify(16),\
},{\
.driver = "qxl",\
.property = "vgamem_mb",\
- .value = stringify(8),\
+ .value = stringify(16),\
},{\
.driver = "virtio-blk-pci",\
.property = "config-wce",\
.value = "off",\
+ },{ \
+ .driver = "qxl", \
+ .property = "rom_size", \
+ .value = stringify(8192), \
+ },{\
+ .driver = "qxl-vga", \
+ .property = "rom_size", \
+ .value = stringify(8192), \
}
+
static QEMUMachine pc_machine_v1_1 = {
.name = "pc-1.1",
.desc = "Standard PC",
diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c
index 4e5bf0b..cbc00a0 100644
--- a/hw/timer/i8254_common.c
+++ b/hw/timer/i8254_common.c
@@ -267,7 +267,12 @@ static const VMStateDescription vmstate_pit_common = {
.pre_save = pit_dispatch_pre_save,
.post_load = pit_dispatch_post_load,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
+ /* qemu-kvm version_id=2 had 'flags' here which is equivalent
+ * This fixes incoming migration from qemu-kvm 1.0, but breaks
+ * incoming migration from qemu < 1.1
+ */
+ //VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
+ VMSTATE_UINT32(channels[0].irq_disabled, PITCommonState),
VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
vmstate_pit_channel, PITChannelState),
VMSTATE_INT64(channels[0].next_transition_time,

View File

@ -0,0 +1,35 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Tue, 13 Aug 2013 00:02:18 +0200
Subject: [PATCH] isapc: disable kvmvapic
vapic requires the VAPIC ROM to be mapped into RAM. This is not
possible without PAM hardware. This fixes a segmentation fault
running with -M isapc.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(crobinso: s/kvmvapic/vapic/g)
Signed-off-by: Cole Robinson <crobinso@redhat.com>
---
hw/i386/pc_piix.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 28216ee..2f2cb4d 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -795,7 +795,11 @@ static QEMUMachine isapc_machine = {
.init = pc_init_isa,
.max_cpus = 1,
.compat_props = (GlobalProperty[]) {
- { /* end of list */ }
+ {
+ .driver = "apic-common",
+ .property = "vapic",
+ .value = "off",
+ },
},
DEFAULT_MACHINE_OPTIONS,
};

View File

@ -0,0 +1,71 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Thu, 2 May 2013 11:38:37 +0200
Subject: [PATCH] pci: do not export pci_bus_reset
qbus_reset_all can be used instead. There is no semantic change
because pcibus_reset returns 1 and takes care of the device
tree traversal.
This will be necessary once the traversal is done always in
qbus_reset_all *before* invoking pcibus_reset itself.
Tested-by: Claudio Bley <cbley@av-test.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/pci/pci.c | 8 ++------
hw/pci/pci_bridge.c | 2 +-
include/hw/pci/pci.h | 1 -
3 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index dc5b788..3a0c4a7 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -212,8 +212,9 @@ void pci_device_reset(PCIDevice *dev)
* Trigger pci bus reset under a given bus.
* To be called on RST# assert.
*/
-void pci_bus_reset(PCIBus *bus)
+static int pcibus_reset(BusState *qbus)
{
+ PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
int i;
for (i = 0; i < bus->nirq; i++) {
@@ -224,11 +225,6 @@ void pci_bus_reset(PCIBus *bus)
pci_device_reset(bus->devices[i]);
}
}
-}
-
-static int pcibus_reset(BusState *qbus)
-{
- pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
/* topology traverse is done by pci_bus_reset().
Tell qbus/qdev walker not to traverse the tree */
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index a90671d..5d0e5ff 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -268,7 +268,7 @@ void pci_bridge_write_config(PCIDevice *d,
newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL);
if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) {
/* Trigger hot reset on 0->1 transition. */
- pci_bus_reset(&s->sec_bus);
+ qbus_reset_all(&s->sec_bus.qbus);
}
}
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index ccec2ba..32f1419 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -376,7 +376,6 @@ void pci_bus_fire_intx_routing_notifier(PCIBus *bus);
void pci_device_set_intx_routing_notifier(PCIDevice *dev,
PCIINTxRoutingNotifier notifier);
void pci_device_reset(PCIDevice *dev);
-void pci_bus_reset(PCIBus *bus);
PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
const char *default_model,

View File

@ -0,0 +1,140 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Thu, 2 May 2013 11:38:38 +0200
Subject: [PATCH] qdev: allow both pre- and post-order vists in qdev walking
functions
Resetting should be done in post-order, not pre-order. However,
qdev_walk_children and qbus_walk_children do not allow this. Fix
it by adding two extra arguments to the functions.
Tested-by: Claudio Bley <cbley@av-test.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/core/qdev.c | 45 +++++++++++++++++++++++++++++++++------------
include/hw/qdev-core.h | 13 +++++++++----
2 files changed, 42 insertions(+), 16 deletions(-)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 9190a7e..842804f 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -240,12 +240,12 @@ static int qbus_reset_one(BusState *bus, void *opaque)
void qdev_reset_all(DeviceState *dev)
{
- qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+ qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
}
void qbus_reset_all(BusState *bus)
{
- qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+ qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
}
void qbus_reset_all_fn(void *opaque)
@@ -343,49 +343,70 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
return NULL;
}
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque)
+int qbus_walk_children(BusState *bus,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque)
{
BusChild *kid;
int err;
- if (busfn) {
- err = busfn(bus, opaque);
+ if (pre_busfn) {
+ err = pre_busfn(bus, opaque);
if (err) {
return err;
}
}
QTAILQ_FOREACH(kid, &bus->children, sibling) {
- err = qdev_walk_children(kid->child, devfn, busfn, opaque);
+ err = qdev_walk_children(kid->child,
+ pre_devfn, pre_busfn,
+ post_devfn, post_busfn, opaque);
if (err < 0) {
return err;
}
}
+ if (post_busfn) {
+ err = post_busfn(bus, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
return 0;
}
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque)
+int qdev_walk_children(DeviceState *dev,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque)
{
BusState *bus;
int err;
- if (devfn) {
- err = devfn(dev, opaque);
+ if (pre_devfn) {
+ err = pre_devfn(dev, opaque);
if (err) {
return err;
}
}
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- err = qbus_walk_children(bus, devfn, busfn, opaque);
+ err = qbus_walk_children(bus, pre_devfn, pre_busfn,
+ post_devfn, post_busfn, opaque);
if (err < 0) {
return err;
}
}
+ if (post_devfn) {
+ err = post_devfn(dev, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
return 0;
}
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 46972f4..c6c9b14 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -270,10 +270,15 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
* < 0 if either devfn or busfn terminate walk somewhere in cursion,
* 0 otherwise. */
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque);
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque);
+int qbus_walk_children(BusState *bus,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque);
+int qdev_walk_children(DeviceState *dev,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque);
+
void qdev_reset_all(DeviceState *dev);
/**

View File

@ -0,0 +1,142 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Thu, 2 May 2013 11:38:39 +0200
Subject: [PATCH] qdev: switch reset to post-order
Post-order is the only sensible direction for the reset signals.
For example, suppose pre-order is used and the parent has some data
structures that cache children state (for example a list of active
requests). When the reset method is invoked on the parent, these caches
could be in any state.
If post-order is used, on the other hand, these will be in a known state
when the reset method is invoked on the parent.
This change means that it is no longer possible to block the visit of
the devices, so the callback is changed to return void. This is not
a problem, because PCI was returning 1 exactly in order to achieve the
same ordering that this patch implements.
PCI can then rely on the qdev core having sent a "reset signal"
(whatever that means) to the device, and only do the PCI-specific
initialization with the new function pci_do_device_reset, extracted
from pci_device_reset. There is no change in the operation of FLR,
which used and still uses pci_device_reset.
Tested-by: Claudio Bley <cbley@av-test.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/core/qdev.c | 6 +++---
hw/pci/pci.c | 31 ++++++++++++++++---------------
include/hw/qdev-core.h | 2 +-
3 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 842804f..87d7e1e 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -233,19 +233,19 @@ static int qbus_reset_one(BusState *bus, void *opaque)
{
BusClass *bc = BUS_GET_CLASS(bus);
if (bc->reset) {
- return bc->reset(bus);
+ bc->reset(bus);
}
return 0;
}
void qdev_reset_all(DeviceState *dev)
{
- qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
+ qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
}
void qbus_reset_all(BusState *bus)
{
- qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
+ qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
}
void qbus_reset_all_fn(void *opaque)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 3a0c4a7..129cdb7 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -46,7 +46,7 @@
static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *pcibus_get_dev_path(DeviceState *dev);
static char *pcibus_get_fw_dev_path(DeviceState *dev);
-static int pcibus_reset(BusState *qbus);
+static void pcibus_reset(BusState *qbus);
static void pci_bus_finalize(Object *obj);
static Property pci_props[] = {
@@ -167,16 +167,10 @@ void pci_device_deassert_intx(PCIDevice *dev)
}
}
-/*
- * This function is called on #RST and FLR.
- * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
- */
-void pci_device_reset(PCIDevice *dev)
+static void pci_do_device_reset(PCIDevice *dev)
{
int r;
- qdev_reset_all(&dev->qdev);
-
dev->irq_state = 0;
pci_update_irq_status(dev);
pci_device_deassert_intx(dev);
@@ -209,10 +203,21 @@ void pci_device_reset(PCIDevice *dev)
}
/*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
+{
+ qdev_reset_all(&dev->qdev);
+ pci_do_device_reset(dev);
+}
+
+/*
* Trigger pci bus reset under a given bus.
- * To be called on RST# assert.
+ * Called via qbus_reset_all on RST# assert, after the devices
+ * have been reset qdev_reset_all-ed already.
*/
-static int pcibus_reset(BusState *qbus)
+static void pcibus_reset(BusState *qbus)
{
PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
int i;
@@ -222,13 +227,9 @@ static int pcibus_reset(BusState *qbus)
}
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
if (bus->devices[i]) {
- pci_device_reset(bus->devices[i]);
+ pci_do_device_reset(bus->devices[i]);
}
}
-
- /* topology traverse is done by pci_bus_reset().
- Tell qbus/qdev walker not to traverse the tree */
- return 1;
}
static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index c6c9b14..89dcbad 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -174,7 +174,7 @@ struct BusClass {
* bindings can be found at http://playground.sun.com/1275/bindings/.
*/
char *(*get_fw_dev_path)(DeviceState *dev);
- int (*reset)(BusState *bus);
+ void (*reset)(BusState *bus);
/* maximum devices allowed on the bus, 0: no limit. */
int max_dev;
};

View File

@ -0,0 +1,250 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:50 +0200
Subject: [PATCH] virtio-bus: remove vdev field
The vdev field is complicated to synchronize. Just access the
BusState's list of children.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/virtio/virtio-bus.c | 67 ++++++++++++++++++++++++------------------
hw/virtio/virtio-mmio.c | 9 +++---
hw/virtio/virtio-pci.c | 2 +-
include/hw/virtio/virtio-bus.h | 16 +++++++---
4 files changed, 57 insertions(+), 37 deletions(-)
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index 6849a01..669ce38 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -46,8 +46,6 @@ int virtio_bus_plug_device(VirtIODevice *vdev)
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
DPRINTF("%s: plug device.\n", qbus->name);
- bus->vdev = vdev;
-
if (klass->device_plugged != NULL) {
klass->device_plugged(qbus->parent);
}
@@ -58,75 +56,84 @@ int virtio_bus_plug_device(VirtIODevice *vdev)
/* Reset the virtio_bus */
void virtio_bus_reset(VirtioBusState *bus)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+
DPRINTF("%s: reset device.\n", qbus->name);
- if (bus->vdev != NULL) {
- virtio_reset(bus->vdev);
+ if (vdev != NULL) {
+ virtio_reset(vdev);
}
}
/* Destroy the VirtIODevice */
void virtio_bus_destroy_device(VirtioBusState *bus)
{
- DeviceState *qdev;
BusState *qbus = BUS(bus);
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+
DPRINTF("%s: remove device.\n", qbus->name);
- if (bus->vdev != NULL) {
+ if (vdev != NULL) {
if (klass->device_unplug != NULL) {
klass->device_unplug(qbus->parent);
}
- qdev = DEVICE(bus->vdev);
- qdev_free(qdev);
- bus->vdev = NULL;
+ qdev_free(DEVICE(vdev));
}
}
/* Get the device id of the plugged device. */
uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus)
{
- assert(bus->vdev != NULL);
- return bus->vdev->device_id;
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ assert(vdev != NULL);
+ return vdev->device_id;
}
/* Get the config_len field of the plugged device. */
size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
{
- assert(bus->vdev != NULL);
- return bus->vdev->config_len;
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ assert(vdev != NULL);
+ return vdev->config_len;
}
/* Get the features of the plugged device. */
uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus,
uint32_t requested_features)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
assert(k->get_features != NULL);
- return k->get_features(bus->vdev, requested_features);
+ return k->get_features(vdev, requested_features);
}
/* Set the features of the plugged device. */
void virtio_bus_set_vdev_features(VirtioBusState *bus,
uint32_t requested_features)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->set_features != NULL) {
- k->set_features(bus->vdev, requested_features);
+ k->set_features(vdev, requested_features);
}
}
/* Get bad features of the plugged device. */
uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->bad_features != NULL) {
- return k->bad_features(bus->vdev);
+ return k->bad_features(vdev);
} else {
return 0;
}
@@ -135,22 +142,26 @@ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
/* Get config of the plugged device. */
void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->get_config != NULL) {
- k->get_config(bus->vdev, config);
+ k->get_config(vdev, config);
}
}
/* Set config of the plugged device. */
void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->set_config != NULL) {
- k->set_config(bus->vdev, config);
+ k->set_config(vdev, config);
}
}
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index 4bd2953..8f7b764 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -94,7 +94,7 @@ static void virtio_mmio_bus_new(VirtioBusState *bus, VirtIOMMIOProxy *dev);
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
- VirtIODevice *vdev = proxy->bus.vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset);
@@ -184,7 +184,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
- VirtIODevice *vdev = proxy->bus.vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n",
(int)offset, value);
@@ -297,12 +297,13 @@ static const MemoryRegionOps virtio_mem_ops = {
static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int level;
- if (!proxy->bus.vdev) {
+ if (!vdev) {
return;
}
- level = (proxy->bus.vdev->isr != 0);
+ level = (vdev->isr != 0);
DPRINTF("virtio_mmio setting IRQ %d\n", level);
qemu_set_irq(proxy->irq, level);
}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 41b96ce..55617a6 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -942,7 +942,7 @@ static void virtio_pci_device_plugged(DeviceState *d)
uint8_t *config;
uint32_t size;
- proxy->vdev = bus->vdev;
+ proxy->vdev = virtio_bus_get_device(bus);
config = proxy->pci_dev.config;
if (proxy->class_code) {
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
index 9217f85..ba0f86a 100644
--- a/include/hw/virtio/virtio-bus.h
+++ b/include/hw/virtio/virtio-bus.h
@@ -72,10 +72,6 @@ typedef struct VirtioBusClass {
struct VirtioBusState {
BusState parent_obj;
- /*
- * Only one VirtIODevice can be plugged on the bus.
- */
- VirtIODevice *vdev;
};
int virtio_bus_plug_device(VirtIODevice *vdev);
@@ -98,4 +94,16 @@ void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config);
/* Set config of the plugged device. */
void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config);
+static inline VirtIODevice *virtio_bus_get_device(VirtioBusState *bus)
+{
+ BusState *qbus = &bus->parent_obj;
+ BusChild *kid = QTAILQ_FIRST(&qbus->children);
+ DeviceState *qdev = kid ? kid->child : NULL;
+
+ /* This is used on the data path, the cast is guaranteed
+ * to succeed by the qdev machinery.
+ */
+ return (VirtIODevice *)qdev;
+}
+
#endif /* VIRTIO_BUS_H */

View File

@ -0,0 +1,446 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:51 +0200
Subject: [PATCH] virtio-pci: remove vdev field
The vdev field is complicated to synchronize. Just access the
BusState's list of children.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/s390x/virtio-ccw.h | 1 -
hw/virtio/virtio-pci.c | 107 +++++++++++++++++++++++++++++--------------------
hw/virtio/virtio-pci.h | 1 -
3 files changed, 63 insertions(+), 46 deletions(-)
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 96d6f5d..00932c7 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -77,7 +77,6 @@ typedef struct VirtIOCCWDeviceClass {
struct VirtioCcwDevice {
DeviceState parent_obj;
SubchDev *sch;
- VirtIODevice *vdev;
char *bus_id;
uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
VirtioBusState bus;
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 55617a6..6fd6d6d 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -112,31 +112,39 @@ static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d)
static void virtio_pci_notify(DeviceState *d, uint16_t vector)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
if (msix_enabled(&proxy->pci_dev))
msix_notify(&proxy->pci_dev, vector);
else
- qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1);
+ qemu_set_irq(proxy->pci_dev.irq[0], vdev->isr & 1);
}
static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
pci_device_save(&proxy->pci_dev, f);
msix_save(&proxy->pci_dev, f);
if (msix_present(&proxy->pci_dev))
- qemu_put_be16(f, proxy->vdev->config_vector);
+ qemu_put_be16(f, vdev->config_vector);
}
static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
if (msix_present(&proxy->pci_dev))
- qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n));
+ qemu_put_be16(f, virtio_queue_vector(vdev, n));
}
static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
int ret;
ret = pci_device_load(&proxy->pci_dev, f);
if (ret) {
@@ -145,12 +153,12 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
msix_unuse_all_vectors(&proxy->pci_dev);
msix_load(&proxy->pci_dev, f);
if (msix_present(&proxy->pci_dev)) {
- qemu_get_be16s(f, &proxy->vdev->config_vector);
+ qemu_get_be16s(f, &vdev->config_vector);
} else {
- proxy->vdev->config_vector = VIRTIO_NO_VECTOR;
+ vdev->config_vector = VIRTIO_NO_VECTOR;
}
- if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
- return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
+ if (vdev->config_vector != VIRTIO_NO_VECTOR) {
+ return msix_vector_use(&proxy->pci_dev, vdev->config_vector);
}
return 0;
}
@@ -158,13 +166,15 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
uint16_t vector;
if (msix_present(&proxy->pci_dev)) {
qemu_get_be16s(f, &vector);
} else {
vector = VIRTIO_NO_VECTOR;
}
- virtio_queue_set_vector(proxy->vdev, n, vector);
+ virtio_queue_set_vector(vdev, n, vector);
if (vector != VIRTIO_NO_VECTOR) {
return msix_vector_use(&proxy->pci_dev, vector);
}
@@ -174,7 +184,8 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
int n, bool assign, bool set_handler)
{
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
int r = 0;
@@ -199,6 +210,7 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
{
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int n, r;
if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
@@ -208,7 +220,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
}
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(proxy->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -222,7 +234,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
assign_error:
while (--n >= 0) {
- if (!virtio_queue_get_num(proxy->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -235,6 +247,7 @@ assign_error:
static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
{
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int r;
int n;
@@ -243,7 +256,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
}
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(proxy->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -256,7 +269,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
VirtIOPCIProxy *proxy = opaque;
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
hwaddr pa;
switch (addr) {
@@ -271,7 +284,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
if (pa == 0) {
virtio_pci_stop_ioeventfd(proxy);
- virtio_reset(proxy->vdev);
+ virtio_reset(vdev);
msix_unuse_all_vectors(&proxy->pci_dev);
}
else
@@ -298,7 +311,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
if (vdev->status == 0) {
- virtio_reset(proxy->vdev);
+ virtio_reset(vdev);
msix_unuse_all_vectors(&proxy->pci_dev);
}
@@ -334,7 +347,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
{
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
uint32_t ret = 0xFFFFFFFF;
switch (addr) {
@@ -380,6 +393,7 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
uint64_t val = 0;
if (addr < config) {
@@ -389,16 +403,16 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
switch (size) {
case 1:
- val = virtio_config_readb(proxy->vdev, addr);
+ val = virtio_config_readb(vdev, addr);
break;
case 2:
- val = virtio_config_readw(proxy->vdev, addr);
+ val = virtio_config_readw(vdev, addr);
if (virtio_is_big_endian()) {
val = bswap16(val);
}
break;
case 4:
- val = virtio_config_readl(proxy->vdev, addr);
+ val = virtio_config_readl(vdev, addr);
if (virtio_is_big_endian()) {
val = bswap32(val);
}
@@ -412,6 +426,7 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr,
{
VirtIOPCIProxy *proxy = opaque;
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (addr < config) {
virtio_ioport_write(proxy, addr, val);
return;
@@ -423,19 +438,19 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr,
*/
switch (size) {
case 1:
- virtio_config_writeb(proxy->vdev, addr, val);
+ virtio_config_writeb(vdev, addr, val);
break;
case 2:
if (virtio_is_big_endian()) {
val = bswap16(val);
}
- virtio_config_writew(proxy->vdev, addr, val);
+ virtio_config_writew(vdev, addr, val);
break;
case 4:
if (virtio_is_big_endian()) {
val = bswap32(val);
}
- virtio_config_writel(proxy->vdev, addr, val);
+ virtio_config_writel(vdev, addr, val);
break;
}
}
@@ -454,6 +469,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
uint32_t val, int len)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
pci_default_write_config(pci_dev, address, val, len);
@@ -461,8 +477,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) &&
!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) {
virtio_pci_stop_ioeventfd(proxy);
- virtio_set_status(proxy->vdev,
- proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
+ virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
}
}
@@ -505,7 +520,8 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
unsigned int vector)
{
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
int ret;
ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, irqfd->virq);
@@ -516,7 +532,8 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
unsigned int queue_no,
unsigned int vector)
{
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
int ret;
@@ -528,7 +545,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)
{
PCIDevice *dev = &proxy->pci_dev;
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
unsigned int vector;
int ret, queue_no;
@@ -577,7 +594,7 @@ undo:
static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)
{
PCIDevice *dev = &proxy->pci_dev;
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
unsigned int vector;
int queue_no;
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
@@ -605,8 +622,9 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
unsigned int vector,
MSIMessage msg)
{
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd;
int ret = 0;
@@ -625,10 +643,10 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
* Otherwise, set it up now.
*/
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(proxy->vdev, queue_no, false);
+ k->guest_notifier_mask(vdev, queue_no, false);
/* Test after unmasking to avoid losing events. */
if (k->guest_notifier_pending &&
- k->guest_notifier_pending(proxy->vdev, queue_no)) {
+ k->guest_notifier_pending(vdev, queue_no)) {
event_notifier_set(n);
}
} else {
@@ -641,13 +659,14 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy,
unsigned int queue_no,
unsigned int vector)
{
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
/* If guest supports masking, keep irqfd but mask it.
* Otherwise, clean it up now.
*/
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(proxy->vdev, queue_no, true);
+ k->guest_notifier_mask(vdev, queue_no, true);
} else {
kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
}
@@ -657,7 +676,7 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
MSIMessage msg)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int ret, queue_no;
for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
@@ -687,7 +706,7 @@ undo:
static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int queue_no;
for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
@@ -706,7 +725,7 @@ static void virtio_pci_vector_poll(PCIDevice *dev,
unsigned int vector_end)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
int queue_no;
unsigned int vector;
@@ -738,8 +757,9 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
bool with_irqfd)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
- VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
if (assign) {
@@ -754,7 +774,7 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
}
if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) {
- vdc->guest_notifier_mask(proxy->vdev, n, !assign);
+ vdc->guest_notifier_mask(vdev, n, !assign);
}
return 0;
@@ -769,7 +789,7 @@ static bool virtio_pci_query_guest_notifiers(DeviceState *d)
static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
int r, n;
bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
@@ -863,11 +883,12 @@ static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
static void virtio_pci_vmstate_change(DeviceState *d, bool running)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (running) {
/* Try to find out if the guest has bus master disabled, but is
in ready state. Then we have a buggy guest OS. */
- if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ if ((vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
!(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
}
@@ -942,8 +963,6 @@ static void virtio_pci_device_plugged(DeviceState *d)
uint8_t *config;
uint32_t size;
- proxy->vdev = virtio_bus_get_device(bus);
-
config = proxy->pci_dev.config;
if (proxy->class_code) {
pci_config_set_class(config, proxy->class_code);
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 917bcc5..dc332ae 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -82,7 +82,6 @@ typedef struct VirtioPCIClass {
struct VirtIOPCIProxy {
PCIDevice pci_dev;
- VirtIODevice *vdev;
MemoryRegion bar;
uint32_t flags;
uint32_t class_code;

View File

@ -0,0 +1,292 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:52 +0200
Subject: [PATCH] virtio-ccw: remove vdev field
The vdev field is complicated to synchronize. Just access the
BusState's list of children.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/s390x/virtio-ccw.c | 80 ++++++++++++++++++++++++++++-----------------------
1 file changed, 44 insertions(+), 36 deletions(-)
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 8835bd4..0fc7387 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -56,9 +56,10 @@ static const TypeInfo virtual_css_bus_info = {
VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
{
VirtIODevice *vdev = NULL;
+ VirtioCcwDevice *dev = sch->driver_data;
- if (sch->driver_data) {
- vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev;
+ if (dev) {
+ vdev = virtio_bus_get_device(&dev->bus);
}
return vdev;
}
@@ -66,7 +67,8 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
bool assign, bool set_handler)
{
- VirtQueue *vq = virtio_get_queue(dev->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
int r = 0;
SubchDev *sch = dev->sch;
@@ -96,6 +98,7 @@ static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
{
+ VirtIODevice *vdev;
int n, r;
if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) ||
@@ -103,8 +106,9 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
dev->ioeventfd_started) {
return;
}
+ vdev = virtio_bus_get_device(&dev->bus);
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(dev->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
r = virtio_ccw_set_guest2host_notifier(dev, n, true, true);
@@ -117,7 +121,7 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
assign_error:
while (--n >= 0) {
- if (!virtio_queue_get_num(dev->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
@@ -131,13 +135,15 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
{
+ VirtIODevice *vdev;
int n, r;
if (!dev->ioeventfd_started) {
return;
}
+ vdev = virtio_bus_get_device(&dev->bus);
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(dev->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
@@ -188,7 +194,7 @@ typedef struct VirtioFeatDesc {
static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
uint16_t index, uint16_t num)
{
- VirtioCcwDevice *dev = sch->driver_data;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
if (index > VIRTIO_PCI_QUEUE_MAX) {
return -EINVAL;
@@ -199,23 +205,23 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
return -EINVAL;
}
- if (!dev) {
+ if (!vdev) {
return -EINVAL;
}
- virtio_queue_set_addr(dev->vdev, index, addr);
+ virtio_queue_set_addr(vdev, index, addr);
if (!addr) {
- virtio_queue_set_vector(dev->vdev, index, 0);
+ virtio_queue_set_vector(vdev, index, 0);
} else {
/* Fail if we don't have a big enough queue. */
/* TODO: Add interface to handle vring.num changing */
- if (virtio_queue_get_num(dev->vdev, index) > num) {
+ if (virtio_queue_get_num(vdev, index) > num) {
return -EINVAL;
}
- virtio_queue_set_vector(dev->vdev, index, index);
+ virtio_queue_set_vector(vdev, index, index);
}
/* tell notify handler in case of config change */
- dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
+ vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
return 0;
}
@@ -229,6 +235,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
hwaddr indicators;
VqConfigBlock vq_config;
VirtioCcwDevice *dev = sch->driver_data;
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
bool check_len;
int len;
hwaddr hw_len;
@@ -271,7 +278,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
break;
case CCW_CMD_VDEV_RESET:
virtio_ccw_stop_ioeventfd(dev);
- virtio_reset(dev->vdev);
+ virtio_reset(vdev);
ret = 0;
break;
case CCW_CMD_READ_FEAT:
@@ -318,7 +325,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
features.features = ldl_le_phys(ccw.cda);
if (features.index < ARRAY_SIZE(dev->host_features)) {
virtio_bus_set_vdev_features(&dev->bus, features.features);
- dev->vdev->guest_features = features.features;
+ vdev->guest_features = features.features;
} else {
/*
* If the guest supports more feature bits, assert that it
@@ -336,30 +343,30 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
break;
case CCW_CMD_READ_CONF:
if (check_len) {
- if (ccw.count > dev->vdev->config_len) {
+ if (ccw.count > vdev->config_len) {
ret = -EINVAL;
break;
}
}
- len = MIN(ccw.count, dev->vdev->config_len);
+ len = MIN(ccw.count, vdev->config_len);
if (!ccw.cda) {
ret = -EFAULT;
} else {
- virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config);
+ virtio_bus_get_vdev_config(&dev->bus, vdev->config);
/* XXX config space endianness */
- cpu_physical_memory_write(ccw.cda, dev->vdev->config, len);
+ cpu_physical_memory_write(ccw.cda, vdev->config, len);
sch->curr_status.scsw.count = ccw.count - len;
ret = 0;
}
break;
case CCW_CMD_WRITE_CONF:
if (check_len) {
- if (ccw.count > dev->vdev->config_len) {
+ if (ccw.count > vdev->config_len) {
ret = -EINVAL;
break;
}
}
- len = MIN(ccw.count, dev->vdev->config_len);
+ len = MIN(ccw.count, vdev->config_len);
hw_len = len;
if (!ccw.cda) {
ret = -EFAULT;
@@ -370,9 +377,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
} else {
len = hw_len;
/* XXX config space endianness */
- memcpy(dev->vdev->config, config, len);
+ memcpy(vdev->config, config, len);
cpu_physical_memory_unmap(config, hw_len, 0, hw_len);
- virtio_bus_set_vdev_config(&dev->bus, dev->vdev->config);
+ virtio_bus_set_vdev_config(&dev->bus, vdev->config);
sch->curr_status.scsw.count = ccw.count - len;
ret = 0;
}
@@ -396,9 +403,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
virtio_ccw_stop_ioeventfd(dev);
}
- virtio_set_status(dev->vdev, status);
- if (dev->vdev->status == 0) {
- virtio_reset(dev->vdev);
+ virtio_set_status(vdev, status);
+ if (vdev->status == 0) {
+ virtio_reset(vdev);
}
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
virtio_ccw_start_ioeventfd(dev);
@@ -462,7 +469,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
ret = -EFAULT;
} else {
vq_config.index = lduw_phys(ccw.cda);
- vq_config.num_max = virtio_queue_get_num(dev->vdev,
+ vq_config.num_max = virtio_queue_get_num(vdev,
vq_config.index);
stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max);
sch->curr_status.scsw.count = ccw.count - sizeof(vq_config);
@@ -494,7 +501,6 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
sch->driver_data = dev;
dev->sch = sch;
- dev->vdev = vdev;
dev->indicators = 0;
/* Initialize subchannel structure. */
@@ -607,7 +613,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
memset(&sch->id, 0, sizeof(SenseId));
sch->id.reserved = 0xff;
sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
- sch->id.cu_model = dev->vdev->device_id;
+ sch->id.cu_model = vdev->device_id;
/* Only the first 32 feature bits are used. */
dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus,
@@ -891,9 +897,10 @@ static unsigned virtio_ccw_get_features(DeviceState *d)
static void virtio_ccw_reset(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
virtio_ccw_stop_ioeventfd(dev);
- virtio_reset(dev->vdev);
+ virtio_reset(vdev);
css_reset_sch(dev->sch);
dev->indicators = 0;
dev->indicators2 = 0;
@@ -933,9 +940,10 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
bool assign, bool with_irqfd)
{
- VirtQueue *vq = virtio_get_queue(dev->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(dev->vdev);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (assign) {
int r = event_notifier_init(notifier, 0);
@@ -951,16 +959,16 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
* land in qemu (and only the irq fd) in this code.
*/
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(dev->vdev, n, false);
+ k->guest_notifier_mask(vdev, n, false);
}
/* get lost events and re-inject */
if (k->guest_notifier_pending &&
- k->guest_notifier_pending(dev->vdev, n)) {
+ k->guest_notifier_pending(vdev, n)) {
event_notifier_set(notifier);
}
} else {
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(dev->vdev, n, true);
+ k->guest_notifier_mask(vdev, n, true);
}
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
event_notifier_cleanup(notifier);
@@ -972,7 +980,7 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
bool assigned)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- VirtIODevice *vdev = dev->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
int r, n;
for (n = 0; n < nvqs; n++) {

View File

@ -0,0 +1,147 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:53 +0200
Subject: [PATCH] virtio-bus: cleanup plug/unplug interface
Right now we have these pairs:
- virtio_bus_plug_device/virtio_bus_destroy_device. The first
takes a VirtIODevice, the second takes a VirtioBusState
- device_plugged/device_unplug callbacks in the VirtioBusClass
(here it's just the naming that is inconsistent)
- virtio_bus_destroy_device is not called by anyone (and since
it calls qdev_free, it would be called by the proxies---but
then the callback is useless since the proxies can do whatever
they want before calling virtio_bus_destroy_device)
And there is a k->init but no k->exit, hence virtio_device_exit is
overwritten by subclasses (except virtio-9p). This cleans it up by:
- renaming the device_unplug callback to device_unplugged
- renaming virtio_bus_plug_device to virtio_bus_device_plugged,
matching the callback name
- renaming virtio_bus_destroy_device to virtio_bus_device_unplugged,
removing the qdev_free, making it take a VirtIODevice and calling it
from virtio_device_exit
- adding a k->exit callback
virtio_device_exit is still overwritten, the next patches will fix that.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/virtio/virtio-bus.c | 18 +++++++++---------
hw/virtio/virtio.c | 7 ++++++-
include/hw/virtio/virtio-bus.h | 6 +++---
include/hw/virtio/virtio.h | 1 +
4 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index 669ce38..7aed6a4 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -37,8 +37,8 @@ do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do { } while (0)
#endif
-/* Plug the VirtIODevice */
-int virtio_bus_plug_device(VirtIODevice *vdev)
+/* A VirtIODevice is being plugged */
+int virtio_bus_device_plugged(VirtIODevice *vdev)
{
DeviceState *qdev = DEVICE(vdev);
BusState *qbus = BUS(qdev_get_parent_bus(qdev));
@@ -64,20 +64,20 @@ void virtio_bus_reset(VirtioBusState *bus)
}
}
-/* Destroy the VirtIODevice */
-void virtio_bus_destroy_device(VirtioBusState *bus)
+/* A VirtIODevice is being unplugged */
+void virtio_bus_device_unplugged(VirtIODevice *vdev)
{
- BusState *qbus = BUS(bus);
+ DeviceState *qdev = DEVICE(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(qdev));
+ VirtioBusState *bus = VIRTIO_BUS(qbus);
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
- VirtIODevice *vdev = virtio_bus_get_device(bus);
DPRINTF("%s: remove device.\n", qbus->name);
if (vdev != NULL) {
- if (klass->device_unplug != NULL) {
- klass->device_unplug(qbus->parent);
+ if (klass->device_unplugged != NULL) {
+ klass->device_unplugged(qbus->parent);
}
- qdev_free(DEVICE(vdev));
}
}
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 2f1e73b..965b2c0 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1158,14 +1158,19 @@ static int virtio_device_init(DeviceState *qdev)
if (k->init(vdev) < 0) {
return -1;
}
- virtio_bus_plug_device(vdev);
+ virtio_bus_device_plugged(vdev);
return 0;
}
static int virtio_device_exit(DeviceState *qdev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(qdev);
+ virtio_bus_device_unplugged(vdev);
+ if (k->exit) {
+ k->exit(vdev);
+ }
if (vdev->bus_name) {
g_free(vdev->bus_name);
vdev->bus_name = NULL;
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
index ba0f86a..0756545 100644
--- a/include/hw/virtio/virtio-bus.h
+++ b/include/hw/virtio/virtio-bus.h
@@ -61,7 +61,7 @@ typedef struct VirtioBusClass {
* transport independent exit function.
* This is called by virtio-bus just before the device is unplugged.
*/
- void (*device_unplug)(DeviceState *d);
+ void (*device_unplugged)(DeviceState *d);
/*
* Does the transport have variable vring alignment?
* (ie can it ever call virtio_queue_set_align()?)
@@ -74,9 +74,9 @@ struct VirtioBusState {
BusState parent_obj;
};
-int virtio_bus_plug_device(VirtIODevice *vdev);
+int virtio_bus_device_plugged(VirtIODevice *vdev);
void virtio_bus_reset(VirtioBusState *bus);
-void virtio_bus_destroy_device(VirtioBusState *bus);
+void virtio_bus_device_unplugged(VirtIODevice *bus);
/* Get the device id of the plugged device. */
uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus);
/* Get the config_len field of the plugged device. */
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index a90522d..59756c2 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -127,6 +127,7 @@ typedef struct VirtioDeviceClass {
/* This is what a VirtioDevice must implement */
DeviceClass parent;
int (*init)(VirtIODevice *vdev);
+ void (*exit)(VirtIODevice *vdev);
uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
uint32_t (*bad_features)(VirtIODevice *vdev);
void (*set_features)(VirtIODevice *vdev, uint32_t val);

View File

@ -0,0 +1,52 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:54 +0200
Subject: [PATCH] virtio-blk: switch exit callback to VirtioDeviceClass
This ensures hot-unplug is handled properly by the proxy.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/block/virtio-blk.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 49a23c3..aa37cc9 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -729,20 +729,18 @@ static int virtio_blk_device_init(VirtIODevice *vdev)
return 0;
}
-static int virtio_blk_device_exit(DeviceState *dev)
+static void virtio_blk_device_exit(VirtIODevice *vdev)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(dev);
- VirtIOBlock *s = VIRTIO_BLK(dev);
+ VirtIOBlock *s = VIRTIO_BLK(vdev);
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
remove_migration_state_change_notifier(&s->migration_state_notifier);
virtio_blk_data_plane_destroy(s->dataplane);
s->dataplane = NULL;
#endif
qemu_del_vm_change_state_handler(s->change);
- unregister_savevm(dev, "virtio-blk", s);
+ unregister_savevm(DEVICE(vdev), "virtio-blk", s);
blockdev_mark_auto_del(s->bs);
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_blk_properties[] = {
@@ -754,10 +752,10 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_blk_device_exit;
dc->props = virtio_blk_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->init = virtio_blk_device_init;
+ vdc->exit = virtio_blk_device_exit;
vdc->get_config = virtio_blk_update_config;
vdc->set_config = virtio_blk_set_config;
vdc->get_features = virtio_blk_get_features;

View File

@ -0,0 +1,52 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:55 +0200
Subject: [PATCH] virtio-serial: switch exit callback to VirtioDeviceClass
This ensures hot-unplug is handled properly by the proxy.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/char/virtio-serial-bus.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index da417c7..57dd070 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -987,12 +987,11 @@ static const TypeInfo virtio_serial_port_type_info = {
.class_init = virtio_serial_port_class_init,
};
-static int virtio_serial_device_exit(DeviceState *dev)
+static void virtio_serial_device_exit(VirtIODevice *vdev)
{
- VirtIOSerial *vser = VIRTIO_SERIAL(dev);
- VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSerial *vser = VIRTIO_SERIAL(vdev);
- unregister_savevm(dev, "virtio-console", vser);
+ unregister_savevm(DEVICE(vdev), "virtio-console", vser);
g_free(vser->ivqs);
g_free(vser->ovqs);
@@ -1004,7 +1003,6 @@ static int virtio_serial_device_exit(DeviceState *dev)
g_free(vser->post_load);
}
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_serial_properties[] = {
@@ -1016,10 +1014,10 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_serial_device_exit;
dc->props = virtio_serial_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
vdc->init = virtio_serial_device_init;
+ vdc->exit = virtio_serial_device_exit;
vdc->get_features = get_features;
vdc->get_config = get_config;
vdc->set_config = set_config;

View File

@ -0,0 +1,57 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:56 +0200
Subject: [PATCH] virtio-net: switch exit callback to VirtioDeviceClass
This ensures hot-unplug is handled properly by the proxy.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/net/virtio-net.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 5320aab..060b900 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1568,16 +1568,15 @@ static int virtio_net_device_init(VirtIODevice *vdev)
return 0;
}
-static int virtio_net_device_exit(DeviceState *qdev)
+static void virtio_net_device_exit(VirtIODevice *vdev)
{
- VirtIONet *n = VIRTIO_NET(qdev);
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIONet *n = VIRTIO_NET(vdev);
int i;
/* This will stop vhost backend if appropriate. */
virtio_net_set_status(vdev, 0);
- unregister_savevm(qdev, "virtio-net", n);
+ unregister_savevm(DEVICE(vdev), "virtio-net", n);
if (n->netclient_name) {
g_free(n->netclient_name);
@@ -1608,8 +1607,6 @@ static int virtio_net_device_exit(DeviceState *qdev)
g_free(n->vqs);
qemu_del_nic(n->nic);
virtio_cleanup(vdev);
-
- return 0;
}
static void virtio_net_instance_init(Object *obj)
@@ -1636,10 +1633,10 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_net_device_exit;
dc->props = virtio_net_properties;
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
vdc->init = virtio_net_device_init;
+ vdc->exit = virtio_net_device_exit;
vdc->get_config = virtio_net_get_config;
vdc->set_config = virtio_net_set_config;
vdc->get_features = virtio_net_get_features;

View File

@ -0,0 +1,111 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:57 +0200
Subject: [PATCH] virtio-scsi: switch exit callback to VirtioDeviceClass
This ensures hot-unplug is handled properly by the proxy.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/scsi/vhost-scsi.c | 11 +++++------
hw/scsi/virtio-scsi.c | 15 +++++++--------
include/hw/virtio/virtio-scsi.h | 2 +-
3 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 9e770fb..5e3cc61 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -240,11 +240,10 @@ static int vhost_scsi_init(VirtIODevice *vdev)
return 0;
}
-static int vhost_scsi_exit(DeviceState *qdev)
+static void vhost_scsi_exit(VirtIODevice *vdev)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
- VHostSCSI *s = VHOST_SCSI(qdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
+ VHostSCSI *s = VHOST_SCSI(vdev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
migrate_del_blocker(s->migration_blocker);
error_free(s->migration_blocker);
@@ -253,7 +252,7 @@ static int vhost_scsi_exit(DeviceState *qdev)
vhost_scsi_set_status(vdev, 0);
g_free(s->dev.vqs);
- return virtio_scsi_common_exit(vs);
+ virtio_scsi_common_exit(vs);
}
static Property vhost_scsi_properties[] = {
@@ -265,10 +264,10 @@ static void vhost_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = vhost_scsi_exit;
dc->props = vhost_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->init = vhost_scsi_init;
+ vdc->exit = vhost_scsi_exit;
vdc->get_features = vhost_scsi_get_features;
vdc->set_config = vhost_scsi_set_config;
vdc->set_status = vhost_scsi_set_status;
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 05da56b..5545993 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -643,22 +643,21 @@ static int virtio_scsi_device_init(VirtIODevice *vdev)
return 0;
}
-int virtio_scsi_common_exit(VirtIOSCSICommon *vs)
+void virtio_scsi_common_exit(VirtIOSCSICommon *vs)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vs);
g_free(vs->cmd_vqs);
virtio_cleanup(vdev);
- return 0;
}
-static int virtio_scsi_device_exit(DeviceState *qdev)
+static void virtio_scsi_device_exit(VirtIODevice *vdev)
{
- VirtIOSCSI *s = VIRTIO_SCSI(qdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
- unregister_savevm(qdev, "virtio-scsi", s);
- return virtio_scsi_common_exit(vs);
+ unregister_savevm(DEVICE(vdev), "virtio-scsi", s);
+ virtio_scsi_common_exit(vs);
}
static Property virtio_scsi_properties[] = {
@@ -679,10 +678,10 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_scsi_device_exit;
dc->props = virtio_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->init = virtio_scsi_device_init;
+ vdc->exit = virtio_scsi_device_exit;
vdc->set_config = virtio_scsi_set_config;
vdc->get_features = virtio_scsi_get_features;
vdc->reset = virtio_scsi_reset;
diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h
index 9a98540..206c61d 100644
--- a/include/hw/virtio/virtio-scsi.h
+++ b/include/hw/virtio/virtio-scsi.h
@@ -187,6 +187,6 @@ typedef struct {
VIRTIO_SCSI_F_CHANGE, true)
int virtio_scsi_common_init(VirtIOSCSICommon *vs);
-int virtio_scsi_common_exit(VirtIOSCSICommon *vs);
+void virtio_scsi_common_exit(VirtIOSCSICommon *vs);
#endif /* _QEMU_VIRTIO_SCSI_H */

View File

@ -0,0 +1,48 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:58 +0200
Subject: [PATCH] virtio-balloon: switch exit callback to VirtioDeviceClass
This ensures hot-unplug is handled properly by the proxy.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/virtio/virtio-balloon.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index aac7f83..c23facb 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -370,16 +370,14 @@ static int virtio_balloon_device_init(VirtIODevice *vdev)
return 0;
}
-static int virtio_balloon_device_exit(DeviceState *qdev)
+static void virtio_balloon_device_exit(VirtIODevice *vdev)
{
- VirtIOBalloon *s = VIRTIO_BALLOON(qdev);
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
balloon_stats_destroy_timer(s);
qemu_remove_balloon_handler(s);
- unregister_savevm(qdev, "virtio-balloon", s);
+ unregister_savevm(DEVICE(vdev), "virtio-balloon", s);
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_balloon_properties[] = {
@@ -390,10 +388,10 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_balloon_device_exit;
dc->props = virtio_balloon_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
vdc->init = virtio_balloon_device_init;
+ vdc->exit = virtio_balloon_device_exit;
vdc->get_config = virtio_balloon_get_config;
vdc->set_config = virtio_balloon_set_config;
vdc->get_features = virtio_balloon_get_features;

View File

@ -0,0 +1,48 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:57:59 +0200
Subject: [PATCH] virtio-rng: switch exit callback to VirtioDeviceClass
This ensures hot-unplug is handled properly by the proxy.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/virtio/virtio-rng.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
index bac8421..6895146 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -184,16 +184,14 @@ static int virtio_rng_device_init(VirtIODevice *vdev)
return 0;
}
-static int virtio_rng_device_exit(DeviceState *qdev)
+static void virtio_rng_device_exit(VirtIODevice *vdev)
{
- VirtIORNG *vrng = VIRTIO_RNG(qdev);
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIORNG *vrng = VIRTIO_RNG(vdev);
qemu_del_timer(vrng->rate_limit_timer);
qemu_free_timer(vrng->rate_limit_timer);
- unregister_savevm(qdev, "virtio-rng", vrng);
+ unregister_savevm(DEVICE(vdev), "virtio-rng", vrng);
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_rng_properties[] = {
@@ -205,10 +203,10 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_rng_device_exit;
dc->props = virtio_rng_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
vdc->init = virtio_rng_device_init;
+ vdc->exit = virtio_rng_device_exit;
vdc->get_features = get_features;
}

View File

@ -0,0 +1,58 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 16:58:00 +0200
Subject: [PATCH] virtio-pci: add device_unplugged callback
This fixes a crash in hot-unplug of virtio-pci devices behind a PCIe
switch. The crash happens because the ioeventfd is still set whent the
child is destroyed (destruction happens in postorder). Then the proxy
tries to unset to ioeventfd, but the virtqueue structure that holds the
EventNotifier has been trashed in the meanwhile. kvm_set_ioeventfd_pio
does not expect failure and aborts.
The fix is simply to move parts of uninitialization to a new
device_unplugged callback, which is called before the child is destroyed.
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/virtio/virtio-pci.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 6fd6d6d..242ec3e 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1000,6 +1000,15 @@ static void virtio_pci_device_plugged(DeviceState *d)
proxy->host_features);
}
+static void virtio_pci_device_unplugged(DeviceState *d)
+{
+ PCIDevice *pci_dev = PCI_DEVICE(d);
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
+
+ virtio_pci_stop_ioeventfd(proxy);
+ msix_uninit_exclusive_bar(pci_dev);
+}
+
static int virtio_pci_init(PCIDevice *pci_dev)
{
VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
@@ -1014,9 +1023,7 @@ static int virtio_pci_init(PCIDevice *pci_dev)
static void virtio_pci_exit(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
- virtio_pci_stop_ioeventfd(proxy);
memory_region_destroy(&proxy->bar);
- msix_uninit_exclusive_bar(pci_dev);
}
static void virtio_pci_reset(DeviceState *qdev)
@@ -1550,6 +1557,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
k->vmstate_change = virtio_pci_vmstate_change;
k->device_plugged = virtio_pci_device_plugged;
+ k->device_unplugged = virtio_pci_device_unplugged;
}
static const TypeInfo virtio_pci_bus_info = {

View File

@ -0,0 +1,80 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Fri, 6 Sep 2013 12:32:25 +0200
Subject: [PATCH] qcow2: Pass discard type to qcow2_discard_clusters()
The function will be used internally instead of only being called for
guest discard requests.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/qcow2-cluster.c | 8 ++++----
block/qcow2.c | 2 +-
block/qcow2.h | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index b558eb0..09abbf0 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1320,7 +1320,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
* clusters.
*/
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
- unsigned int nb_clusters)
+ unsigned int nb_clusters, enum qcow2_discard_type type)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table;
@@ -1349,7 +1349,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
l2_table[l2_index + i] = cpu_to_be64(0);
/* Then decrease the refcount */
- qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
+ qcow2_free_any_clusters(bs, old_offset, 1, type);
}
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
@@ -1361,7 +1361,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
}
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
- int nb_sectors)
+ int nb_sectors, enum qcow2_discard_type type)
{
BDRVQcowState *s = bs->opaque;
uint64_t end_offset;
@@ -1384,7 +1384,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
/* Each L2 table is handled by its own loop iteration */
while (nb_clusters > 0) {
- ret = discard_single_l2(bs, offset, nb_clusters);
+ ret = discard_single_l2(bs, offset, nb_clusters, type);
if (ret < 0) {
goto fail;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index 3bf932b..f87e6e3 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1510,7 +1510,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
qemu_co_mutex_lock(&s->lock);
ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
- nb_sectors);
+ nb_sectors, QCOW2_DISCARD_REQUEST);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
diff --git a/block/qcow2.h b/block/qcow2.h
index dba9771..52cf193 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -405,7 +405,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
- int nb_sectors);
+ int nb_sectors, enum qcow2_discard_type type);
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
/* qcow2-snapshot.c functions */

View File

@ -0,0 +1,72 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Fri, 6 Sep 2013 12:32:26 +0200
Subject: [PATCH] qcow2: Discard VM state in active L1 after creating snapshot
During savevm, the VM state is written to the active L1 of the image and
then a snapshot is taken. After that, the VM state isn't needed any more
in the active L1 and should be discarded. This is implemented by this
patch.
The impact of not discarding the VM state is that a snapshot can never
become smaller than any previous snapshot (because it would be padded
with old VM state), and more importantly that future savevm operations
cause unnecessary COWs (with associated flushes), which makes subsequent
snapshots much slower.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/qcow2-snapshot.c | 7 +++++++
block/qcow2.c | 5 -----
block/qcow2.h | 5 +++++
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index 0caac90..ae33b45 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -401,6 +401,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
g_free(old_snapshot_list);
+ /* The VM state isn't needed any more in the active L1 table; in fact, it
+ * hurts by causing expensive COW for the next snapshot. */
+ qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
+ align_offset(sn->vm_state_size, s->cluster_size)
+ >> BDRV_SECTOR_BITS,
+ QCOW2_DISCARD_NEVER);
+
#ifdef DEBUG_ALLOC
{
BdrvCheckResult result = {0};
diff --git a/block/qcow2.c b/block/qcow2.c
index f87e6e3..44161b2 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1670,11 +1670,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return 0;
}
-static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
-{
- return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
-}
-
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BDRVQcowState *s = bs->opaque;
diff --git a/block/qcow2.h b/block/qcow2.h
index 52cf193..da61d18 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -324,6 +324,11 @@ static inline int64_t align_offset(int64_t offset, int n)
return offset;
}
+static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
+{
+ return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
+}
+
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
{
if (l2_entry & QCOW_OFLAG_COMPRESSED) {

View File

@ -0,0 +1,67 @@
From: "Daniel P. Berrange" <berrange@redhat.com>
Date: Tue, 1 Oct 2013 12:28:17 +0100
Subject: [PATCH] hw/9pfs: Fix errno value for xattr functions
If there is no operation driver for the xattr type the
functions return '-1' and set errno to '-EOPNOTSUPP'.
When the calling code sets 'ret = -errno' this turns
into a large positive number.
In Linux 3.11, the kernel has switched to using 9p
version 9p2000.L, instead of 9p2000.u, which enables
support for xattr operations. This on its own is harmless,
but for another change which makes it request the xattr
with a name 'security.capability'.
The result is that the guest sees a succesful return
of 95 bytes of data, instead of a failure with errno
set to 95. Since the kernel expects a maximum of 20
bytes for an xattr return this gets translated to the
unexpected errno ERANGE.
This all means that when running a binary off a 9p fs
in 3.11 kernels you get a fun result of:
# ./date
sh: ./date: Numerical result out of range
The only workaround is to pass 'version=9p2000.u' when
mounting the 9p fs in the guest, to disable all use of
xattrs.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
hw/9pfs/virtio-9p-xattr.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c
index 90ae565..3fae557 100644
--- a/hw/9pfs/virtio-9p-xattr.c
+++ b/hw/9pfs/virtio-9p-xattr.c
@@ -36,7 +36,7 @@ ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
if (xops) {
return xops->getxattr(ctx, path, name, value, size);
}
- errno = -EOPNOTSUPP;
+ errno = EOPNOTSUPP;
return -1;
}
@@ -123,7 +123,7 @@ int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
if (xops) {
return xops->setxattr(ctx, path, name, value, size, flags);
}
- errno = -EOPNOTSUPP;
+ errno = EOPNOTSUPP;
return -1;
}
@@ -135,7 +135,7 @@ int v9fs_remove_xattr(FsContext *ctx,
if (xops) {
return xops->removexattr(ctx, path, name);
}
- errno = -EOPNOTSUPP;
+ errno = EOPNOTSUPP;
return -1;
}

View File

@ -0,0 +1,180 @@
From: Amos Kong <akong@redhat.com>
Date: Fri, 15 Nov 2013 18:53:14 +0100
Subject: [PATCH] qmp: access the local QemuOptsLists for drive option
Currently we have three QemuOptsList (qemu_common_drive_opts,
qemu_legacy_drive_opts, and qemu_drive_opts), only qemu_drive_opts
is added to vm_config_groups[].
This patch changes query-command-line-options to access three local
QemuOptsLists for drive option, and merge the description items
together.
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
blockdev.c | 1 -
include/qemu/config-file.h | 1 +
include/sysemu/sysemu.h | 1 +
util/qemu-config.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-
vl.c | 2 ++
5 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/blockdev.c b/blockdev.c
index 097932c..1a6892e 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -45,7 +45,6 @@
#include "sysemu/arch_init.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
-extern QemuOptsList qemu_common_drive_opts;
extern QemuOptsList qemu_old_drive_opts;
static const char *const if_name[IF_COUNT] = {
diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
index ad4a9e5..508428f 100644
--- a/include/qemu/config-file.h
+++ b/include/qemu/config-file.h
@@ -8,6 +8,7 @@
QemuOptsList *qemu_find_opts(const char *group);
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
void qemu_add_opts(QemuOptsList *list);
+void qemu_add_drive_opts(QemuOptsList *list);
int qemu_set_option(const char *str);
int qemu_global_option(const char *str);
void qemu_add_globals(void);
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 1a77c99..4962cef 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -190,6 +190,7 @@ QemuOpts *qemu_get_machine_opts(void);
bool usb_enabled(bool default_usb);
+extern QemuOptsList qemu_common_drive_opts;
extern QemuOptsList qemu_drive_opts;
extern QemuOptsList qemu_chardev_opts;
extern QemuOptsList qemu_device_opts;
diff --git a/util/qemu-config.c b/util/qemu-config.c
index a59568d..04da942 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -8,6 +8,7 @@
#include "qmp-commands.h"
static QemuOptsList *vm_config_groups[32];
+static QemuOptsList *drive_config_groups[4];
static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
Error **errp)
@@ -77,6 +78,59 @@ static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc)
return param_list;
}
+/* remove repeated entry from the info list */
+static void cleanup_infolist(CommandLineParameterInfoList *head)
+{
+ CommandLineParameterInfoList *pre_entry, *cur, *del_entry;
+
+ cur = head;
+ while (cur->next) {
+ pre_entry = head;
+ while (pre_entry != cur->next) {
+ if (!strcmp(pre_entry->value->name, cur->next->value->name)) {
+ del_entry = cur->next;
+ cur->next = cur->next->next;
+ g_free(del_entry);
+ break;
+ }
+ pre_entry = pre_entry->next;
+ }
+ cur = cur->next;
+ }
+}
+
+/* merge the description items of two parameter infolists */
+static void connect_infolist(CommandLineParameterInfoList *head,
+ CommandLineParameterInfoList *new)
+{
+ CommandLineParameterInfoList *cur;
+
+ cur = head;
+ while (cur->next) {
+ cur = cur->next;
+ }
+ cur->next = new;
+}
+
+/* access all the local QemuOptsLists for drive option */
+static CommandLineParameterInfoList *get_drive_infolist(void)
+{
+ CommandLineParameterInfoList *head = NULL, *cur;
+ int i;
+
+ for (i = 0; drive_config_groups[i] != NULL; i++) {
+ if (!head) {
+ head = query_option_descs(drive_config_groups[i]->desc);
+ } else {
+ cur = query_option_descs(drive_config_groups[i]->desc);
+ connect_infolist(head, cur);
+ }
+ }
+ cleanup_infolist(head);
+
+ return head;
+}
+
CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option,
const char *option,
Error **errp)
@@ -89,7 +143,12 @@ CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option,
if (!has_option || !strcmp(option, vm_config_groups[i]->name)) {
info = g_malloc0(sizeof(*info));
info->option = g_strdup(vm_config_groups[i]->name);
- info->parameters = query_option_descs(vm_config_groups[i]->desc);
+ if (!strcmp("drive", vm_config_groups[i]->name)) {
+ info->parameters = get_drive_infolist();
+ } else {
+ info->parameters =
+ query_option_descs(vm_config_groups[i]->desc);
+ }
entry = g_malloc0(sizeof(*entry));
entry->value = info;
entry->next = conf_list;
@@ -109,6 +168,22 @@ QemuOptsList *qemu_find_opts_err(const char *group, Error **errp)
return find_list(vm_config_groups, group, errp);
}
+void qemu_add_drive_opts(QemuOptsList *list)
+{
+ int entries, i;
+
+ entries = ARRAY_SIZE(drive_config_groups);
+ entries--; /* keep list NULL terminated */
+ for (i = 0; i < entries; i++) {
+ if (drive_config_groups[i] == NULL) {
+ drive_config_groups[i] = list;
+ return;
+ }
+ }
+ fprintf(stderr, "ran out of space in drive_config_groups");
+ abort();
+}
+
void qemu_add_opts(QemuOptsList *list)
{
int entries, i;
diff --git a/vl.c b/vl.c
index 2160933..63ecf16 100644
--- a/vl.c
+++ b/vl.c
@@ -2942,6 +2942,8 @@ int main(int argc, char **argv, char **envp)
module_call_init(MODULE_INIT_QOM);
qemu_add_opts(&qemu_drive_opts);
+ qemu_add_drive_opts(&qemu_common_drive_opts);
+ qemu_add_drive_opts(&qemu_drive_opts);
qemu_add_opts(&qemu_chardev_opts);
qemu_add_opts(&qemu_device_opts);
qemu_add_opts(&qemu_netdev_opts);

View File

@ -0,0 +1,27 @@
From: Eduardo Otubo <otubo@linux.vnet.ibm.com>
Date: Tue, 24 Sep 2013 14:50:44 -0300
Subject: [PATCH] seccomp: fine tuning whitelist by adding times()
This was causing Qemu process to hang when using -sandbox on as
discribed on RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1004175
Signed-off-by: Eduardo Otubo <otubo@linux.vnet.ibm.com>
Tested-by: Paul Moore <pmoore@redhat.com>
Acked-by: Paul Moore <pmoore@redhat.com>
(cherry picked from commit c236f4519c9838801798f3705c17dce9ab9e3b9d)
---
qemu-seccomp.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/qemu-seccomp.c b/qemu-seccomp.c
index fb3cbfd..cf07869 100644
--- a/qemu-seccomp.c
+++ b/qemu-seccomp.c
@@ -90,6 +90,7 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = {
{ SCMP_SYS(getuid), 245 },
{ SCMP_SYS(geteuid), 245 },
{ SCMP_SYS(timer_create), 245 },
+ { SCMP_SYS(times), 245 },
{ SCMP_SYS(exit), 245 },
{ SCMP_SYS(clock_gettime), 245 },
{ SCMP_SYS(time), 245 },

View File

@ -0,0 +1,32 @@
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Mon, 2 Dec 2013 11:17:04 +0100
Subject: [PATCH] spice: flip streaming video mode to off by default
Video streaming detection heuristics in spice-server have problems
keeping modern desktop animations (as done by gnome shell) and real
video playback apart. This leads to jpeg compression artefacts on
your desktop, due to spice using mjpeg to send what it thinks is
a video stream.
Turn off video detection by default to avoid these artifacts.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Alon Levy <alevy@redhat.com>
(cherry picked from commit f1d3e586f069e17f83b669842bc02d60d509daca)
---
ui/spice-core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ui/spice-core.c b/ui/spice-core.c
index bd7a248..3960fa0 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -778,6 +778,8 @@ void qemu_spice_init(void)
if (str) {
int streaming_video = parse_stream_video(str);
spice_server_set_streaming_video(spice_server, streaming_video);
+ } else {
+ spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
}
spice_server_set_agent_mouse

View File

@ -0,0 +1,58 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Thu, 28 Nov 2013 11:01:13 +0100
Subject: [PATCH] scsi-bus: fix transfer length and direction for VERIFY
command
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The amount of bytes to transfer depends on the BYTCHK field.
If any data is transferred, it is sent to the device.
Cc: qemu-stable@nongnu.org
Tested-by: Hervé Poussineau <hpoussin@reactos.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit d12ad44cc4cc9142179e64295608611f118b8ad8)
---
hw/scsi/scsi-bus.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index d352da7..e2fcd60 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -886,7 +886,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case RELEASE:
case ERASE:
case ALLOW_MEDIUM_REMOVAL:
- case VERIFY_10:
case SEEK_10:
case SYNCHRONIZE_CACHE:
case SYNCHRONIZE_CACHE_16:
@@ -903,6 +902,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case ALLOW_OVERWRITE:
cmd->xfer = 0;
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ if ((buf[1] & 2) == 0) {
+ cmd->xfer = 0;
+ } else if ((buf[1] & 4) == 1) {
+ cmd->xfer = 1;
+ }
+ cmd->xfer *= dev->blocksize;
+ break;
case MODE_SENSE:
break;
case WRITE_SAME_10:
@@ -1100,6 +1109,9 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
case WRITE_VERIFY_12:
case WRITE_16:
case WRITE_VERIFY_16:
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
case COPY:
case COPY_VERIFY:
case COMPARE:

View File

@ -0,0 +1,89 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Thu, 28 Nov 2013 11:18:56 +0100
Subject: [PATCH] scsi-disk: fix VERIFY emulation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
VERIFY emulation was completely botched (and remained botched through
all the refactorings). The command must be emulated both in check-medium
mode (BYTCHK=00, which we implement by doing nothing) and in check-bytes
mode (which we do not implement yet). Unlike WRITE AND VERIFY (which we
treat simply as WRITE with FUA bit set), VERIFY cannot be handled like
READ. In fact the device is _receiving_ data for VERIFY, not _sending_
it like READ.
Cc: qemu-stable@nongnu.org
Tested-by: Hervé Poussineau <hpoussin@reactos.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit d97e7730816094a71cd1f19a56d7a73f77cdbf96)
Conflicts:
hw/scsi/scsi-disk.c
---
hw/scsi/scsi-disk.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 74e6a14..1fd1c26 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1597,6 +1597,14 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
scsi_disk_emulate_unmap(r, r->iov.iov_base);
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ if (r->req.status == -1) {
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+ }
+ break;
+
default:
abort();
}
@@ -1837,6 +1845,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
case UNMAP:
DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3);
+ if (req->cmd.buf[1] & 6) {
+ goto illegal_request;
+ }
+ break;
case WRITE_SAME_10:
case WRITE_SAME_16:
nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
@@ -1936,10 +1952,6 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return 0;
}
- /* fallthrough */
- case VERIFY_10:
- case VERIFY_12:
- case VERIFY_16:
DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
(command & 0xe) == 0xe ? "And Verify " : "",
r->req.cmd.lba, len);
@@ -2207,14 +2219,14 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
[UNMAP] = &scsi_disk_emulate_reqops,
[WRITE_SAME_10] = &scsi_disk_emulate_reqops,
[WRITE_SAME_16] = &scsi_disk_emulate_reqops,
+ [VERIFY_10] = &scsi_disk_emulate_reqops,
+ [VERIFY_12] = &scsi_disk_emulate_reqops,
+ [VERIFY_16] = &scsi_disk_emulate_reqops,
[READ_6] = &scsi_disk_dma_reqops,
[READ_10] = &scsi_disk_dma_reqops,
[READ_12] = &scsi_disk_dma_reqops,
[READ_16] = &scsi_disk_dma_reqops,
- [VERIFY_10] = &scsi_disk_dma_reqops,
- [VERIFY_12] = &scsi_disk_dma_reqops,
- [VERIFY_16] = &scsi_disk_dma_reqops,
[WRITE_6] = &scsi_disk_dma_reqops,
[WRITE_10] = &scsi_disk_dma_reqops,
[WRITE_12] = &scsi_disk_dma_reqops,

View File

@ -0,0 +1,46 @@
From: Peter Lieven <pl@kamp.de>
Date: Thu, 24 Oct 2013 09:21:29 +0200
Subject: [PATCH] migration: drop MADVISE_DONT_NEED for incoming zero pages
The madvise for zeroed out pages was introduced when every transferred
zero page was memset to zero and thus allocated. Since commit
211ea740 we check for zeroness of a target page before we memset
it to zero. Additionally we memmap target memory so it is essentially
zero initialized (except for e.g. option roms and bios which are loaded
into target memory although they shouldn't).
It was reported recently that this madvise causes a performance degradation
in some situations. As the madvise should only be called rarely and if it's called
it is likely on a busy page (it was non-zero and changed to zero during migration)
drop it completely.
Reported-By: Zhang Haoyu <haoyu.zhang@huawei.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Peter Lieven <pl@kamp.de>
Signed-off-by: Juan Quintela <quintela@redhat.com>
(cherry picked from commit fc1c4a5d32e15a4c40c47945da85ef9c1e0c1b54)
Conflicts:
arch_init.c
---
arch_init.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/arch_init.c b/arch_init.c
index 68a7ab7..23151b3 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -845,13 +845,6 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
{
if (ch != 0 || !is_zero_page(host)) {
memset(host, ch, size);
-#ifndef _WIN32
- if (ch == 0 &&
- (!kvm_enabled() || kvm_has_sync_mmu()) &&
- getpagesize() <= TARGET_PAGE_SIZE) {
- qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED);
- }
-#endif
}
}

View File

@ -0,0 +1,29 @@
From: Christophe Fergeau <cfergeau@redhat.com>
Date: Thu, 30 Jan 2014 14:56:49 +0100
Subject: [PATCH] libcacard: Don't link with all libraries QEMU links to
As described in https://bugzilla.redhat.com/show_bug.cgi?id=987441 ,
libcacard currently links to all the libraries QEMU is linking to,
including glusterfs libraries, libiscsi, ... libcacard does not need all of
these. This patch ensures it's only linked with the libraries it needs.
Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
Signed-off-by: Alon Levy <alevy@redhat.com>
(cherry picked from commit 73db416ae7941f8ffeabc060ec87402b97314b6d)
---
libcacard/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcacard/Makefile b/libcacard/Makefile
index 47827a0..9fa297c 100644
--- a/libcacard/Makefile
+++ b/libcacard/Makefile
@@ -24,7 +24,7 @@ vscclient$(EXESUF): libcacard/vscclient.o libcacard.la
libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \
-export-syms $(SRC_PATH)/libcacard/libcacard.syms
-libcacard.la: LIBS += $(libcacard_libs)
+libcacard.la: LIBS = $(libcacard_libs)
libcacard.la: $(libcacard-lobj-y)
$(call LINK,$^)

View File

@ -0,0 +1,32 @@
From: Cole Robinson <crobinso@redhat.com>
Date: Thu, 13 Mar 2014 15:30:23 -0400
Subject: [PATCH] gtk: Fix mouse warping with gtk3
We were using the wrong coordinates, this fixes things to match the
original gtk2 implementation.
You can see this error in action by using -vga qxl, however even after this
patch the mouse warps in small increments up and to the left, -7x and -3y
pixels at a time, until the pointer is warped off the widget. I think it's
a qxl bug, but the next patch covers it up.
Signed-off-by: Cole Robinson <crobinso@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit 298526fe92d0b35ea343f8ddcc3a1d54cb422494)
---
ui/gtk.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/gtk.c b/ui/gtk.c
index c38146f..6c9d90a 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -355,7 +355,7 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
x, y, &x_root, &y_root);
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
gtk_widget_get_screen(s->drawing_area),
- x, y);
+ x_root, y_root);
}
#else
static void gd_mouse_set(DisplayChangeListener *dcl,

View File

@ -0,0 +1,41 @@
From: Cole Robinson <crobinso@redhat.com>
Date: Thu, 13 Mar 2014 15:30:24 -0400
Subject: [PATCH] gtk: Don't warp absolute pointer
This matches the behavior of SDL, and makes the mouse usable when
using -display gtk -vga qxl
https://bugzilla.redhat.com/show_bug.cgi?id=1051724
Signed-off-by: Cole Robinson <crobinso@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit 2bda66028b4962c36d4eabe2995edab12df93691)
---
ui/gtk.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/ui/gtk.c b/ui/gtk.c
index 6c9d90a..6ce9694 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -349,6 +349,10 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
GdkDeviceManager *mgr;
gint x_root, y_root;
+ if (kbd_mouse_is_absolute()) {
+ return;
+ }
+
dpy = gtk_widget_get_display(s->drawing_area);
mgr = gdk_display_get_device_manager(dpy);
gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
@@ -364,6 +368,10 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
gint x_root, y_root;
+ if (kbd_mouse_is_absolute()) {
+ return;
+ }
+
gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
x, y, &x_root, &y_root);
gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area),

View File

@ -0,0 +1,36 @@
From: Cole Robinson <crobinso@redhat.com>
Date: Wed, 19 Mar 2014 14:57:27 -0400
Subject: [PATCH] Change gtk quit accelerator to ctrl+shift+q (bz 1062393)
Similar patches queued for 2.1
---
ui/gtk.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/ui/gtk.c b/ui/gtk.c
index 6ce9694..8bc667d 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1310,7 +1310,6 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce
{
GtkWidget *machine_menu;
GtkWidget *separator;
- GtkStockItem item;
machine_menu = gtk_menu_new();
gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group);
@@ -1330,11 +1329,11 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce
separator = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
- s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
- gtk_stock_lookup(GTK_STOCK_QUIT, &item);
+ s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
"<QEMU>/Machine/Quit");
- gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier);
+ gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
+ GDK_KEY_q, HOTKEY_MODIFIERS);
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
return machine_menu;

View File

@ -0,0 +1,30 @@
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Wed, 15 Jan 2014 10:35:36 +0100
Subject: [PATCH] scsi: Assign cancel_io vector for scsi_disk_emulate_ops
Some emulated disk operations (MODE SELECT, UNMAP, WRITE SAME)
can trigger asynchronous I/Os. Provide the cancel_io callback
to ensure that AIOCBs are properly cleaned up.
Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
Cc: qemu-stable@nongnu.org
[Tweak commit message. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 33325a53f15ab5370e1917b2a11cadffc77c5a52)
---
hw/scsi/scsi-disk.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 1fd1c26..ade5d4a 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2181,6 +2181,7 @@ static const SCSIReqOps scsi_disk_emulate_reqops = {
.send_command = scsi_disk_emulate_command,
.read_data = scsi_disk_emulate_read_data,
.write_data = scsi_disk_emulate_write_data,
+ .cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
};

View File

@ -0,0 +1,38 @@
From: Eric Farman <farman@linux.vnet.ibm.com>
Date: Tue, 14 Jan 2014 14:16:25 -0500
Subject: [PATCH] virtio-scsi: Cleanup of I/Os that never started
There is still a small window that occurs when a cancel I/O affects
an asynchronous I/O operation that hasn't started. In other words,
when the residual data length equals the expected data length.
Today, the routine virtio_scsi_command_complete fails because the
VirtIOSCSIReq pointer (from the hba_private field in SCSIRequest)
was cleared earlier when virtio_scsi_complete_req was called by
the virtio_scsi_request_cancelled routine. As a result, the
virtio_scsi_command_complete routine needs to simply return when
it is processing a SCSIRequest block that was marked canceled.
Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e9c0f0f58ad0a41c3c4b19e1911cfe095afc09ca)
---
hw/scsi/virtio-scsi.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 5545993..110827c 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -306,6 +306,10 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
VirtIOSCSIReq *req = r->hba_private;
uint32_t sense_len;
+ if (r->io_canceled) {
+ return;
+ }
+
req->resp.cmd->response = VIRTIO_SCSI_S_OK;
req->resp.cmd->status = status;
if (req->resp.cmd->status == GOOD) {

View File

@ -0,0 +1,29 @@
From: Eric Farman <farman@linux.vnet.ibm.com>
Date: Tue, 14 Jan 2014 14:16:26 -0500
Subject: [PATCH] virtio-scsi: Prevent assertion on missed events
In some cases, an unplug can cause events to be dropped, which
leads to an assertion failure when preparing to notify the guest
kernel.
Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 49fb65c7f985baa56d2964e0a85c1f098e3e2a9d)
---
hw/scsi/virtio-scsi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 110827c..15e40d9 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -520,7 +520,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
evt->event = event;
evt->reason = reason;
if (!dev) {
- assert(event == VIRTIO_SCSI_T_NO_EVENT);
+ assert(event == VIRTIO_SCSI_T_EVENTS_MISSED);
} else {
evt->lun[0] = 1;
evt->lun[1] = dev->id;

View File

@ -0,0 +1,65 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:25 +0100
Subject: [PATCH] block/cloop: validate block_size header field (CVE-2014-0144)
Avoid unbounded s->uncompressed_block memory allocation by checking that
the block_size header field has a reasonable value. Also enforce the
assumption that the value is a non-zero multiple of 512.
These constraints conform to cloop 2.639's code so we accept existing
image files.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit d65f97a82c4ed48374a764c769d4ba1ea9724e97)
Conflicts:
tests/qemu-iotests/075
tests/qemu-iotests/075.out
---
block/cloop.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/block/cloop.c b/block/cloop.c
index 6ea7cf4..c2441b0 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -26,6 +26,9 @@
#include "qemu/module.h"
#include <zlib.h>
+/* Maximum compressed block size */
+#define MAX_BLOCK_SIZE (64 * 1024 * 1024)
+
typedef struct BDRVCloopState {
CoMutex lock;
uint32_t block_size;
@@ -67,6 +70,26 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
return ret;
}
s->block_size = be32_to_cpu(s->block_size);
+ if (s->block_size % 512) {
+ fprintf(stderr, "block_size %u must be a multiple of 512",
+ s->block_size);
+ return -EINVAL;
+ }
+ if (s->block_size == 0) {
+ fprintf(stderr, "block_size cannot be zero");
+ return -EINVAL;
+ }
+
+ /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but
+ * we can accept more. Prevent ridiculous values like 4 GB - 1 since we
+ * need a buffer this big.
+ */
+ if (s->block_size > MAX_BLOCK_SIZE) {
+ fprintf(stderr, "block_size %u must be %u MB or less",
+ s->block_size,
+ MAX_BLOCK_SIZE / (1024 * 1024));
+ return -EINVAL;
+ }
ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
if (ret < 0) {

View File

@ -0,0 +1,62 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:26 +0100
Subject: [PATCH] block/cloop: prevent offsets_size integer overflow
(CVE-2014-0143)
The following integer overflow in offsets_size can lead to out-of-bounds
memory stores when n_blocks has a huge value:
uint32_t n_blocks, offsets_size;
[...]
ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
[...]
s->n_blocks = be32_to_cpu(s->n_blocks);
/* read offsets */
offsets_size = s->n_blocks * sizeof(uint64_t);
s->offsets = g_malloc(offsets_size);
[...]
for(i=0;i<s->n_blocks;i++) {
s->offsets[i] = be64_to_cpu(s->offsets[i]);
offsets_size can be smaller than n_blocks due to integer overflow.
Therefore s->offsets[] is too small when the for loop byteswaps offsets.
This patch refuses to open files if offsets_size would overflow.
Note that changing the type of offsets_size is not a fix since 32-bit
hosts still only have 32-bit size_t.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 509a41bab5306181044b5fff02eadf96d9c8676a)
Conflicts:
tests/qemu-iotests/075
tests/qemu-iotests/075.out
---
block/cloop.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/block/cloop.c b/block/cloop.c
index c2441b0..e20d0d8 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -98,6 +98,13 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
s->n_blocks = be32_to_cpu(s->n_blocks);
/* read offsets */
+ if (s->n_blocks > UINT32_MAX / sizeof(uint64_t)) {
+ /* Prevent integer overflow */
+ fprintf(stderr, "n_blocks %u must be %zu or less",
+ s->n_blocks,
+ UINT32_MAX / sizeof(uint64_t));
+ return -EINVAL;
+ }
offsets_size = s->n_blocks * sizeof(uint64_t);
s->offsets = g_malloc(offsets_size);

View File

@ -0,0 +1,46 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:27 +0100
Subject: [PATCH] block/cloop: refuse images with huge offsets arrays
(CVE-2014-0144)
Limit offsets_size to 512 MB so that:
1. g_malloc() does not abort due to an unreasonable size argument.
2. offsets_size does not overflow the bdrv_pread() int size argument.
This limit imposes a maximum image size of 16 TB at 256 KB block size.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 7b103b36d6ef3b11827c203d3a793bf7da50ecd6)
Conflicts:
tests/qemu-iotests/075
tests/qemu-iotests/075.out
---
block/cloop.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/block/cloop.c b/block/cloop.c
index e20d0d8..cf81c61 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -106,6 +106,15 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
return -EINVAL;
}
offsets_size = s->n_blocks * sizeof(uint64_t);
+ if (offsets_size > 512 * 1024 * 1024) {
+ /* Prevent ridiculous offsets_size which causes memory allocation to
+ * fail or overflows bdrv_pread() size. In practice the 512 MB
+ * offsets[] limit supports 16 TB images at 256 KB block size.
+ */
+ fprintf(stderr, "image requires too many offsets, "
+ "try increasing block size");
+ return -EINVAL;
+ }
s->offsets = g_malloc(offsets_size);
ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);

View File

@ -0,0 +1,70 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:28 +0100
Subject: [PATCH] block/cloop: refuse images with bogus offsets (CVE-2014-0144)
The offsets[] array allows efficient seeking and tells us the maximum
compressed data size. If the offsets are bogus the maximum compressed
data size will be unrealistic.
This could cause g_malloc() to abort and bogus offsets mean the image is
broken anyway. Therefore we should refuse such images.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit f56b9bc3ae20fc93815b34aa022be919941406ce)
Conflicts:
tests/qemu-iotests/075
tests/qemu-iotests/075.out
---
block/cloop.c | 34 +++++++++++++++++++++++++++++-----
1 file changed, 29 insertions(+), 5 deletions(-)
diff --git a/block/cloop.c b/block/cloop.c
index cf81c61..5c9c085 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -123,12 +123,36 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
}
for(i=0;i<s->n_blocks;i++) {
+ uint64_t size;
+
s->offsets[i] = be64_to_cpu(s->offsets[i]);
- if (i > 0) {
- uint32_t size = s->offsets[i] - s->offsets[i - 1];
- if (size > max_compressed_block_size) {
- max_compressed_block_size = size;
- }
+ if (i == 0) {
+ continue;
+ }
+
+ if (s->offsets[i] < s->offsets[i - 1]) {
+ fprintf(stderr, "offsets not monotonically increasing at "
+ "index %u, image file is corrupt", i);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ size = s->offsets[i] - s->offsets[i - 1];
+
+ /* Compressed blocks should be smaller than the uncompressed block size
+ * but maybe compression performed poorly so the compressed block is
+ * actually bigger. Clamp down on unrealistic values to prevent
+ * ridiculous s->compressed_block allocation.
+ */
+ if (size > 2 * MAX_BLOCK_SIZE) {
+ fprintf(stderr, "invalid compressed block size at index %u, "
+ "image file is corrupt", i);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (size > max_compressed_block_size) {
+ max_compressed_block_size = size;
}
}

View File

@ -0,0 +1,74 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:29 +0100
Subject: [PATCH] block/cloop: fix offsets[] size off-by-one
cloop stores the number of compressed blocks in the n_blocks header
field. The file actually contains n_blocks + 1 offsets, where the extra
offset is the end-of-file offset.
The following line in cloop_read_block() results in an out-of-bounds
offsets[] access:
uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num];
This patch allocates and loads the extra offset so that
cloop_read_block() works correctly when the last block is accessed.
Notice that we must free s->offsets[] unconditionally now since there is
always an end-of-file offset.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 42d43d35d907579179a39c924d169da924786f65)
Conflicts:
tests/qemu-iotests/075
tests/qemu-iotests/075.out
---
block/cloop.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/block/cloop.c b/block/cloop.c
index 5c9c085..b28aae1 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -98,14 +98,14 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
s->n_blocks = be32_to_cpu(s->n_blocks);
/* read offsets */
- if (s->n_blocks > UINT32_MAX / sizeof(uint64_t)) {
+ if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) {
/* Prevent integer overflow */
fprintf(stderr, "n_blocks %u must be %zu or less",
s->n_blocks,
- UINT32_MAX / sizeof(uint64_t));
+ (UINT32_MAX - 1) / sizeof(uint64_t));
return -EINVAL;
}
- offsets_size = s->n_blocks * sizeof(uint64_t);
+ offsets_size = (s->n_blocks + 1) * sizeof(uint64_t);
if (offsets_size > 512 * 1024 * 1024) {
/* Prevent ridiculous offsets_size which causes memory allocation to
* fail or overflows bdrv_pread() size. In practice the 512 MB
@@ -122,7 +122,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
- for(i=0;i<s->n_blocks;i++) {
+ for (i = 0; i < s->n_blocks + 1; i++) {
uint64_t size;
s->offsets[i] = be64_to_cpu(s->offsets[i]);
@@ -242,9 +242,7 @@ static coroutine_fn int cloop_co_read(BlockDriverState *bs, int64_t sector_num,
static void cloop_close(BlockDriverState *bs)
{
BDRVCloopState *s = bs->opaque;
- if (s->n_blocks > 0) {
- g_free(s->offsets);
- }
+ g_free(s->offsets);
g_free(s->compressed_block);
g_free(s->uncompressed_block);
inflateEnd(&s->zstream);

View File

@ -0,0 +1,133 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:31 +0100
Subject: [PATCH] bochs: Unify header structs and make them QEMU_PACKED
This is an on-disk structure, so offsets must be accurate.
Before this patch, sizeof(bochs) != sizeof(header_v1), which makes the
memcpy() between both invalid. We're lucky enough that the destination
buffer happened to be the larger one, and the memcpy size to be taken
from the smaller one, so we didn't get a buffer overflow in practice.
This patch unifies the both structures, eliminating the need to do a
memcpy in the first place. The common fields are extracted to the top
level of the struct and the actually differing part gets a union of the
two versions.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 3dd8a6763bcc50dfc3de8da9279b741c0dea9fb1)
---
block/bochs.c | 67 ++++++++++++++++++++++-------------------------------------
1 file changed, 25 insertions(+), 42 deletions(-)
diff --git a/block/bochs.c b/block/bochs.c
index d7078c0..d550ce1 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -39,45 +39,30 @@
// not allocated: 0xffffffff
// always little-endian
-struct bochs_header_v1 {
- char magic[32]; // "Bochs Virtual HD Image"
- char type[16]; // "Redolog"
- char subtype[16]; // "Undoable" / "Volatile" / "Growing"
- uint32_t version;
- uint32_t header; // size of header
-
- union {
- struct {
- uint32_t catalog; // num of entries
- uint32_t bitmap; // bitmap size
- uint32_t extent; // extent size
- uint64_t disk; // disk size
- char padding[HEADER_SIZE - 64 - 8 - 20];
- } redolog;
- char padding[HEADER_SIZE - 64 - 8];
- } extra;
-};
-
-// always little-endian
struct bochs_header {
- char magic[32]; // "Bochs Virtual HD Image"
- char type[16]; // "Redolog"
- char subtype[16]; // "Undoable" / "Volatile" / "Growing"
+ char magic[32]; /* "Bochs Virtual HD Image" */
+ char type[16]; /* "Redolog" */
+ char subtype[16]; /* "Undoable" / "Volatile" / "Growing" */
uint32_t version;
- uint32_t header; // size of header
+ uint32_t header; /* size of header */
+
+ uint32_t catalog; /* num of entries */
+ uint32_t bitmap; /* bitmap size */
+ uint32_t extent; /* extent size */
union {
- struct {
- uint32_t catalog; // num of entries
- uint32_t bitmap; // bitmap size
- uint32_t extent; // extent size
- uint32_t reserved; // for ???
- uint64_t disk; // disk size
- char padding[HEADER_SIZE - 64 - 8 - 24];
- } redolog;
- char padding[HEADER_SIZE - 64 - 8];
+ struct {
+ uint32_t reserved; /* for ??? */
+ uint64_t disk; /* disk size */
+ char padding[HEADER_SIZE - 64 - 20 - 12];
+ } QEMU_PACKED redolog;
+ struct {
+ uint64_t disk; /* disk size */
+ char padding[HEADER_SIZE - 64 - 20 - 8];
+ } QEMU_PACKED redolog_v1;
+ char padding[HEADER_SIZE - 64 - 20];
} extra;
-};
+} QEMU_PACKED;
typedef struct BDRVBochsState {
CoMutex lock;
@@ -113,7 +98,6 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
BDRVBochsState *s = bs->opaque;
int i;
struct bochs_header bochs;
- struct bochs_header_v1 header_v1;
int ret;
bs->read_only = 1; // no write support yet
@@ -132,13 +116,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
}
if (le32_to_cpu(bochs.version) == HEADER_V1) {
- memcpy(&header_v1, &bochs, sizeof(bochs));
- bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
+ bs->total_sectors = le64_to_cpu(bochs.extra.redolog_v1.disk) / 512;
} else {
- bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
+ bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
}
- s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
+ s->catalog_size = le32_to_cpu(bochs.catalog);
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
@@ -152,10 +135,10 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
- s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
- s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
+ s->bitmap_blocks = 1 + (le32_to_cpu(bochs.bitmap) - 1) / 512;
+ s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512;
- s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
+ s->extent_size = le32_to_cpu(bochs.extent);
qemu_co_mutex_init(&s->lock);
return 0;

View File

@ -0,0 +1,64 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:32 +0100
Subject: [PATCH] bochs: Use unsigned variables for offsets and sizes
(CVE-2014-0147)
Gets us rid of integer overflows resulting in negative sizes which
aren't correctly checked.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 246f65838d19db6db55bfb41117c35645a2c4789)
Conflicts:
tests/qemu-iotests/078
tests/qemu-iotests/078.out
---
block/bochs.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/block/bochs.c b/block/bochs.c
index d550ce1..750cec0 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -67,13 +67,13 @@ struct bochs_header {
typedef struct BDRVBochsState {
CoMutex lock;
uint32_t *catalog_bitmap;
- int catalog_size;
+ uint32_t catalog_size;
- int data_offset;
+ uint32_t data_offset;
- int bitmap_blocks;
- int extent_blocks;
- int extent_size;
+ uint32_t bitmap_blocks;
+ uint32_t extent_blocks;
+ uint32_t extent_size;
} BDRVBochsState;
static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
@@ -96,7 +96,7 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVBochsState *s = bs->opaque;
- int i;
+ uint32_t i;
struct bochs_header bochs;
int ret;
@@ -151,8 +151,8 @@ fail:
static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
{
BDRVBochsState *s = bs->opaque;
- int64_t offset = sector_num * 512;
- int64_t extent_index, extent_offset, bitmap_offset;
+ uint64_t offset = sector_num * 512;
+ uint64_t extent_index, extent_offset, bitmap_offset;
char bitmap_entry;
// seek to sector

View File

@ -0,0 +1,53 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:33 +0100
Subject: [PATCH] bochs: Check catalog_size header field (CVE-2014-0143)
It should neither become negative nor allow unbounded memory
allocations. This fixes aborts in g_malloc() and an s->catalog_bitmap
buffer overflow on big endian hosts.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit e3737b820b45e54b059656dc3f914f895ac7a88b)
Conflicts:
tests/qemu-iotests/078
tests/qemu-iotests/078.out
---
block/bochs.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/block/bochs.c b/block/bochs.c
index 750cec0..4393ecc 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -121,7 +121,14 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
}
+ /* Limit to 1M entries to avoid unbounded allocation. This is what is
+ * needed for the largest image that bximage can create (~8 TB). */
s->catalog_size = le32_to_cpu(bochs.catalog);
+ if (s->catalog_size > 0x100000) {
+ fprintf(stderr, "Catalog size is too large");
+ return -EFBIG;
+ }
+
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
@@ -140,6 +147,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
s->extent_size = le32_to_cpu(bochs.extent);
+ if (s->catalog_size < bs->total_sectors / s->extent_size) {
+ fprintf(stderr, "Catalog size is too small for this disk size");
+ ret = -EINVAL;
+ goto fail;
+ }
+
qemu_co_mutex_init(&s->lock);
return 0;

View File

@ -0,0 +1,39 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:34 +0100
Subject: [PATCH] bochs: Check extent_size header field (CVE-2014-0142)
This fixes two possible division by zero crashes: In bochs_open() and in
seek_to_sector().
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 8e53abbc20d08ae3ec30c2054e1161314ad9501d)
Conflicts:
tests/qemu-iotests/078
tests/qemu-iotests/078.out
---
block/bochs.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/block/bochs.c b/block/bochs.c
index 4393ecc..10fbd39 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -146,6 +146,14 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512;
s->extent_size = le32_to_cpu(bochs.extent);
+ if (s->extent_size == 0) {
+ fprintf(stderr, "Extent size may not be zero");
+ return -EINVAL;
+ } else if (s->extent_size > 0x800000) {
+ fprintf(stderr, "Extent size %" PRIu32 " is too large",
+ s->extent_size);
+ return -EINVAL;
+ }
if (s->catalog_size < bs->total_sectors / s->extent_size) {
fprintf(stderr, "Catalog size is too small for this disk size");

View File

@ -0,0 +1,31 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:35 +0100
Subject: [PATCH] bochs: Fix bitmap offset calculation
32 bit truncation could let us access the wrong offset in the image.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit a9ba36a45dfac645a810c31ce15ab393b69d820a)
---
block/bochs.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/block/bochs.c b/block/bochs.c
index 10fbd39..6749a61 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -184,8 +184,9 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
return -1; /* not allocated */
}
- bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
- (s->extent_blocks + s->bitmap_blocks));
+ bitmap_offset = s->data_offset +
+ (512 * (uint64_t) s->catalog_bitmap[extent_index] *
+ (s->extent_blocks + s->bitmap_blocks));
/* read in bitmap for current extent */
if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),

View File

@ -0,0 +1,96 @@
From: Jeff Cody <jcody@redhat.com>
Date: Wed, 26 Mar 2014 13:05:36 +0100
Subject: [PATCH] vpc/vhd: add bounds check for max_table_entries and
block_size (CVE-2014-0144)
This adds checks to make sure that max_table_entries and block_size
are in sane ranges. Memory is allocated based on max_table_entries,
and block_size is used to calculate indices into that allocated
memory, so if these values are incorrect that can lead to potential
unbounded memory allocation, or invalid memory accesses.
Also, the allocation of the pagetable is changed from g_malloc0()
to qemu_blockalign().
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 97f1c45c6f456572e5b504b8614e4a69e23b8e3a)
---
block/vpc.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/block/vpc.c b/block/vpc.c
index fe4f311..16c5acf 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -45,6 +45,8 @@ enum vhd_type {
// Seconds since Jan 1, 2000 0:00:00 (UTC)
#define VHD_TIMESTAMP_BASE 946684800
+#define VHD_MAX_SECTORS (65535LL * 255 * 255)
+
// always big-endian
struct vhd_footer {
char creator[8]; // "conectix"
@@ -163,6 +165,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
struct vhd_dyndisk_header* dyndisk_header;
uint8_t buf[HEADER_SIZE];
uint32_t checksum;
+ uint64_t computed_size;
int disk_type = VHD_DYNAMIC;
int ret;
@@ -211,7 +214,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
/* Allow a maximum disk size of approximately 2 TB */
- if (bs->total_sectors >= 65535LL * 255 * 255) {
+ if (bs->total_sectors >= VHD_MAX_SECTORS) {
ret = -EFBIG;
goto fail;
}
@@ -234,7 +237,23 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
- s->pagetable = g_malloc(s->max_table_entries * 4);
+
+ if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ computed_size = (uint64_t) s->max_table_entries * s->block_size;
+ if (computed_size < bs->total_sectors * 512) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4);
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
@@ -280,7 +299,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
return 0;
fail:
- g_free(s->pagetable);
+ qemu_vfree(s->pagetable);
#ifdef CACHE
g_free(s->pageentry_u8);
#endif
@@ -801,7 +820,7 @@ static int vpc_has_zero_init(BlockDriverState *bs)
static void vpc_close(BlockDriverState *bs)
{
BDRVVPCState *s = bs->opaque;
- g_free(s->pagetable);
+ qemu_vfree(s->pagetable);
#ifdef CACHE
g_free(s->pageentry_u8);
#endif

View File

@ -0,0 +1,33 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:37 +0100
Subject: [PATCH] vpc: Validate block size (CVE-2014-0142)
This fixes some cases of division by zero crashes.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 5e71dfad763d67bb64be79e20e93411c0c30ad25)
Conflicts:
tests/qemu-iotests/group
---
block/vpc.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/block/vpc.c b/block/vpc.c
index 16c5acf..a41a0ab 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -234,6 +234,11 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
}
s->block_size = be32_to_cpu(dyndisk_header->block_size);
+ if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) {
+ fprintf(stderr, "Invalid block size %" PRIu32, s->block_size);
+ ret = -EINVAL;
+ goto fail;
+ }
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);

View File

@ -0,0 +1,110 @@
From: Jeff Cody <jcody@redhat.com>
Date: Fri, 28 Mar 2014 11:42:24 -0400
Subject: [PATCH] vdi: add bounds checks for blocks_in_image and disk_size
header fields (CVE-2014-0144)
The maximum blocks_in_image is 0xffffffff / 4, which also limits the
maximum disk_size for a VDI image to 1024TB. Note that this is the maximum
size that QEMU will currently support with this driver, not necessarily the
maximum size allowed by the image format.
This also fixes an incorrect error message, a bug introduced by commit
5b7aa9b56d1bfc79916262f380c3fc7961becb50 (Reported by Stefan Weil)
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 63fa06dc978f3669dbfd9443b33cde9e2a7f4b41)
Conflicts:
block/vdi.c
---
block/vdi.c | 35 +++++++++++++++++++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/block/vdi.c b/block/vdi.c
index 8a91525..63d509e 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -120,6 +120,11 @@ typedef unsigned char uuid_t[16];
#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
+/* max blocks in image is (0xffffffff / 4) */
+#define VDI_BLOCKS_IN_IMAGE_MAX 0x3fffffff
+#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
+ (uint64_t)DEFAULT_CLUSTER_SIZE)
+
#if !defined(CONFIG_UUID)
static inline void uuid_generate(uuid_t out)
{
@@ -383,6 +388,14 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
vdi_header_print(&header);
#endif
+ if (header.disk_size > VDI_DISK_SIZE_MAX) {
+ fprintf(stderr, "Unsupported VDI image size (size is 0x%" PRIx64
+ ", max supported is 0x%" PRIx64 ")",
+ header.disk_size, VDI_DISK_SIZE_MAX);
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
if (header.disk_size % SECTOR_SIZE != 0) {
/* 'VBoxManage convertfromraw' can create images with odd disk sizes.
We accept them but round the disk size to the next multiple of
@@ -415,8 +428,10 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
logout("unsupported sector size %u B\n", header.sector_size);
ret = -ENOTSUP;
goto fail;
- } else if (header.block_size != 1 * MiB) {
logout("unsupported block size %u B\n", header.block_size);
+ } else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
+ logout("unsupported VDI image (block size %u is not %u)",
+ header.block_size, DEFAULT_CLUSTER_SIZE);
ret = -ENOTSUP;
goto fail;
} else if (header.disk_size >
@@ -432,6 +447,12 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
logout("parent uuid != 0, unsupported\n");
ret = -ENOTSUP;
goto fail;
+ } else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) {
+ fprintf(stderr, "unsupported VDI image "
+ "(too many blocks %u, max is %u)",
+ header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX);
+ ret = -ENOTSUP;
+ goto fail;
}
bs->total_sectors = header.disk_size / SECTOR_SIZE;
@@ -668,11 +689,20 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
options++;
}
+ if (bytes > VDI_DISK_SIZE_MAX) {
+ result = -ENOTSUP;
+ fprintf(stderr, "Unsupported VDI image size (size is 0x%" PRIx64
+ ", max supported is 0x%" PRIx64 ")",
+ bytes, VDI_DISK_SIZE_MAX);
+ goto exit;
+ }
+
fd = qemu_open(filename,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
0644);
if (fd < 0) {
- return -errno;
+ result = -errno;
+ goto exit;
}
/* We need enough blocks to store the given disk size,
@@ -733,6 +763,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
result = -errno;
}
+exit:
return result;
}

View File

@ -0,0 +1,66 @@
From: Jeff Cody <jcody@redhat.com>
Date: Wed, 26 Mar 2014 13:05:39 +0100
Subject: [PATCH] vhdx: Bounds checking for block_size and logical_sector_size
(CVE-2014-0148)
Other variables (e.g. sectors_per_block) are calculated using these
variables, and if not range-checked illegal values could be obtained
causing infinite loops and other potential issues when calculating
BAT entries.
The 1.00 VHDX spec requires BlockSize to be min 1MB, max 256MB.
LogicalSectorSize is required to be either 512 or 4096 bytes.
Reported-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 1d7678dec4761acdc43439da6ceda41a703ba1a6)
---
block/vhdx.c | 12 ++++++++++--
block/vhdx.h | 4 ++++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/block/vhdx.c b/block/vhdx.c
index e9704b1..36fc06c 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -627,12 +627,20 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
le32_to_cpus(&s->logical_sector_size);
le32_to_cpus(&s->physical_sector_size);
- if (s->logical_sector_size == 0 || s->params.block_size == 0) {
+ if (s->params.block_size < VHDX_BLOCK_SIZE_MIN ||
+ s->params.block_size > VHDX_BLOCK_SIZE_MAX) {
ret = -EINVAL;
goto exit;
}
- /* both block_size and sector_size are guaranteed powers of 2 */
+ /* only 2 supported sector sizes */
+ if (s->logical_sector_size != 512 && s->logical_sector_size != 4096) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Both block_size and sector_size are guaranteed powers of 2, below.
+ Due to range checks above, s->sectors_per_block can never be < 256 */
s->sectors_per_block = s->params.block_size / s->logical_sector_size;
s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) *
(uint64_t)s->logical_sector_size /
diff --git a/block/vhdx.h b/block/vhdx.h
index fb687ed..227ac99 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -280,6 +280,10 @@ typedef struct QEMU_PACKED VHDXPage83Data {
support page 0x83 */
} VHDXPage83Data;
+#define KiB (1 * 1024)
+#define MiB (KiB * 1024)
+#define VHDX_BLOCK_SIZE_MIN (1 * MiB)
+#define VHDX_BLOCK_SIZE_MAX (256 * MiB)
typedef struct QEMU_PACKED VHDXVirtualDiskLogicalSectorSize {
uint32_t logical_sector_size; /* virtual disk sector size (in bytes).
Can only be 512 or 4096 bytes */

View File

@ -0,0 +1,36 @@
From: Fam Zheng <famz@redhat.com>
Date: Wed, 26 Mar 2014 13:05:40 +0100
Subject: [PATCH] curl: check data size before memcpy to local buffer.
(CVE-2014-0144)
curl_read_cb is callback function for libcurl when data arrives. The
data size passed in here is not guaranteed to be within the range of
request we submitted, so we may overflow the guest IO buffer. Check the
real size we have before memcpy to buffer to avoid overflow.
Signed-off-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 6d4b9e55fc625514a38d27cff4b9933f617fa7dc)
---
block/curl.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/block/curl.c b/block/curl.c
index 82d39ff..14ae7e5 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -136,6 +136,11 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
if (!s || !s->orig_buf)
goto read_end;
+ if (s->buf_off >= s->buf_len) {
+ /* buffer full, read nothing */
+ return 0;
+ }
+ realsize = MIN(realsize, s->buf_len - s->buf_off);
memcpy(s->orig_buf + s->buf_off, ptr, realsize);
s->buf_off += realsize;

View File

@ -0,0 +1,83 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:41 +0100
Subject: [PATCH] qcow2: Check header_length (CVE-2014-0144)
This fixes an unbounded allocation for s->unknown_header_fields.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 24342f2cae47d03911e346fe1e520b00dc2818e0)
Conflicts:
block/qcow2.c
tests/qemu-iotests/group
---
block/qcow2.c | 33 +++++++++++++++++++++++++--------
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 44161b2..40867a1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -355,6 +355,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
s->qcow_version = header.version;
+ /* Initialise cluster size */
+ if (header.cluster_bits < MIN_CLUSTER_BITS ||
+ header.cluster_bits > MAX_CLUSTER_BITS) {
+ fprintf(stderr, "Unsupported cluster size: 2^%i", header.cluster_bits);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ s->cluster_bits = header.cluster_bits;
+ s->cluster_size = 1 << s->cluster_bits;
+ s->cluster_sectors = 1 << (s->cluster_bits - 9);
+
/* Initialise version 3 header fields */
if (header.version == 2) {
header.incompatible_features = 0;
@@ -368,6 +380,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
be64_to_cpus(&header.autoclear_features);
be32_to_cpus(&header.refcount_order);
be32_to_cpus(&header.header_length);
+
+ if (header.header_length < 104) {
+ fprintf(stderr, "qcow2 header too short");
+ ret = -EINVAL;
+ goto fail;
+ }
+ }
+
+ if (header.header_length > s->cluster_size) {
+ fprintf(stderr, "qcow2 header exceeds cluster size");
+ ret = -EINVAL;
+ goto fail;
}
if (header.header_length > sizeof(header)) {
@@ -410,11 +434,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
- if (header.cluster_bits < MIN_CLUSTER_BITS ||
- header.cluster_bits > MAX_CLUSTER_BITS) {
- ret = -EINVAL;
- goto fail;
- }
if (header.crypt_method > QCOW_CRYPT_AES) {
ret = -EINVAL;
goto fail;
@@ -423,9 +442,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
if (s->crypt_method_header) {
bs->encrypted = 1;
}
- s->cluster_bits = header.cluster_bits;
- s->cluster_size = 1 << s->cluster_bits;
- s->cluster_sectors = 1 << (s->cluster_bits - 9);
+
s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
s->l2_size = 1 << s->l2_bits;
bs->total_sectors = header.size / 512;

View File

@ -0,0 +1,38 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:42 +0100
Subject: [PATCH] qcow2: Check backing_file_offset (CVE-2014-0144)
Header, header extension and the backing file name must all be stored in
the first cluster. Setting the backing file to a much higher value
allowed header extensions to become much bigger than we want them to be
(unbounded allocation).
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit a1b3955c9415b1e767c130a2f59fee6aa28e575b)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/block/qcow2.c b/block/qcow2.c
index 40867a1..4392111 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -404,6 +404,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
}
}
+ if (header.backing_file_offset > s->cluster_size) {
+ fprintf(stderr, "Invalid backing file offset");
+ ret = -EINVAL;
+ goto fail;
+ }
+
if (header.backing_file_offset) {
ext_end = header.backing_file_offset;
} else {

View File

@ -0,0 +1,61 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:43 +0100
Subject: [PATCH] qcow2: Check refcount table size (CVE-2014-0144)
Limit the in-memory reference count table size to 8 MB, it's enough in
practice. This fixes an unbounded allocation as well as a buffer
overflow in qcow2_refcount_init().
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 5dab2faddc8eaa1fb1abdbe2f502001fc13a1b21)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2-refcount.c | 4 +++-
block/qcow2.c | 9 +++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 1244693..cd641c9 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -38,8 +38,10 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
int qcow2_refcount_init(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
- int ret, refcount_table_size2, i;
+ unsigned int refcount_table_size2, i;
+ int ret;
+ assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t));
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
s->refcount_table = g_malloc(refcount_table_size2);
if (s->refcount_table_size > 0) {
diff --git a/block/qcow2.c b/block/qcow2.c
index 4392111..884262b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -455,10 +455,19 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
s->csize_shift = (62 - (s->cluster_bits - 8));
s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
+
s->refcount_table_offset = header.refcount_table_offset;
s->refcount_table_size =
header.refcount_table_clusters << (s->cluster_bits - 3);
+ if (header.refcount_table_clusters > (0x800000 >> s->cluster_bits)) {
+ /* 8 MB refcount table is enough for 2 PB images at 64k cluster size
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
+ fprintf(stderr, "Reference count table too large");
+ ret = -EINVAL;
+ goto fail;
+ }
+
s->snapshots_offset = header.snapshots_offset;
s->nb_snapshots = header.nb_snapshots;

View File

@ -0,0 +1,74 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:44 +0100
Subject: [PATCH] qcow2: Validate refcount table offset
The end of the refcount table must not exceed INT64_MAX so that integer
overflows are avoided.
Also check for misaligned refcount table. Such images are invalid and
probably the result of data corruption. Error out to avoid further
corruption.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 8c7de28305a514d7f879fdfc677ca11fbf60d2e9)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/block/qcow2.c b/block/qcow2.c
index 884262b..db35ffe 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -286,6 +286,32 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
return ret;
}
+static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
+ uint64_t entries, size_t entry_len)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t size;
+
+ /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
+ * because values will be passed to qemu functions taking int64_t. */
+ if (entries > INT64_MAX / entry_len) {
+ return -EINVAL;
+ }
+
+ size = entries * entry_len;
+
+ if (INT64_MAX - size < offset) {
+ return -EINVAL;
+ }
+
+ /* Tables must be cluster aligned */
+ if (offset & (s->cluster_size - 1)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static QemuOptsList qcow2_runtime_opts = {
.name = "qcow2",
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
@@ -468,6 +494,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
+ ret = validate_table_offset(bs, s->refcount_table_offset,
+ s->refcount_table_size, sizeof(uint64_t));
+ if (ret < 0) {
+ fprintf(stderr, "Invalid reference count table offset");
+ goto fail;
+ }
+
s->snapshots_offset = header.snapshots_offset;
s->nb_snapshots = header.nb_snapshots;

View File

@ -0,0 +1,148 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:45 +0100
Subject: [PATCH] qcow2: Validate snapshot table offset/size (CVE-2014-0144)
This avoid unbounded memory allocation and fixes a potential buffer
overflow on 32 bit hosts.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit ce48f2f441ca98885267af6fd636a7cb804ee646)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2-snapshot.c | 29 ++++-------------------------
block/qcow2.c | 15 +++++++++++++++
block/qcow2.h | 29 ++++++++++++++++++++++++++++-
3 files changed, 47 insertions(+), 26 deletions(-)
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index ae33b45..eb80438 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -26,31 +26,6 @@
#include "block/block_int.h"
#include "block/qcow2.h"
-typedef struct QEMU_PACKED QCowSnapshotHeader {
- /* header is 8 byte aligned */
- uint64_t l1_table_offset;
-
- uint32_t l1_size;
- uint16_t id_str_size;
- uint16_t name_size;
-
- uint32_t date_sec;
- uint32_t date_nsec;
-
- uint64_t vm_clock_nsec;
-
- uint32_t vm_state_size;
- uint32_t extra_data_size; /* for extension */
- /* extra data follows */
- /* id_str follows */
- /* name follows */
-} QCowSnapshotHeader;
-
-typedef struct QEMU_PACKED QCowSnapshotExtraData {
- uint64_t vm_state_size_large;
- uint64_t disk_size;
-} QCowSnapshotExtraData;
-
void qcow2_free_snapshots(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
@@ -326,6 +301,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
uint64_t *l1_table = NULL;
int64_t l1_table_offset;
+ if (s->nb_snapshots >= QCOW_MAX_SNAPSHOTS) {
+ return -EFBIG;
+ }
+
memset(sn, 0, sizeof(*sn));
/* Generate an ID if it wasn't passed */
diff --git a/block/qcow2.c b/block/qcow2.c
index db35ffe..43e2db1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -501,6 +501,21 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
+ /* Snapshot table offset/length */
+ if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
+ fprintf(stderr, "Too many snapshots");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = validate_table_offset(bs, header.snapshots_offset,
+ header.nb_snapshots,
+ sizeof(QCowSnapshotHeader));
+ if (ret < 0) {
+ fprintf(stderr, "Invalid snapshot table offset");
+ goto fail;
+ }
+
s->snapshots_offset = header.snapshots_offset;
s->nb_snapshots = header.nb_snapshots;
diff --git a/block/qcow2.h b/block/qcow2.h
index da61d18..1e1294c 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -38,6 +38,7 @@
#define QCOW_CRYPT_AES 1
#define QCOW_MAX_CRYPT_CLUSTERS 32
+#define QCOW_MAX_SNAPSHOTS 65536
/* indicate that the refcount of the referenced cluster is exactly one. */
#define QCOW_OFLAG_COPIED (1LL << 63)
@@ -88,6 +89,32 @@ typedef struct QCowHeader {
uint32_t header_length;
} QCowHeader;
+typedef struct QEMU_PACKED QCowSnapshotHeader {
+ /* header is 8 byte aligned */
+ uint64_t l1_table_offset;
+
+ uint32_t l1_size;
+ uint16_t id_str_size;
+ uint16_t name_size;
+
+ uint32_t date_sec;
+ uint32_t date_nsec;
+
+ uint64_t vm_clock_nsec;
+
+ uint32_t vm_state_size;
+ uint32_t extra_data_size; /* for extension */
+ /* extra data follows */
+ /* id_str follows */
+ /* name follows */
+} QCowSnapshotHeader;
+
+typedef struct QEMU_PACKED QCowSnapshotExtraData {
+ uint64_t vm_state_size_large;
+ uint64_t disk_size;
+} QCowSnapshotExtraData;
+
+
typedef struct QCowSnapshot {
uint64_t l1_table_offset;
uint32_t l1_size;
@@ -190,7 +217,7 @@ typedef struct BDRVQcowState {
AES_KEY aes_decrypt_key;
uint64_t snapshots_offset;
int snapshots_size;
- int nb_snapshots;
+ unsigned int nb_snapshots;
QCowSnapshot *snapshots;
int flags;

View File

@ -0,0 +1,54 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:46 +0100
Subject: [PATCH] qcow2: Validate active L1 table offset and size
(CVE-2014-0144)
This avoids an unbounded allocation.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 2d51c32c4b511db8bb9e58208f1e2c25e4c06c85)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/block/qcow2.c b/block/qcow2.c
index 43e2db1..8dd285b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -520,6 +520,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
s->nb_snapshots = header.nb_snapshots;
/* read the level 1 table */
+ if (header.l1_size > 0x2000000) {
+ /* 32 MB L1 table is enough for 2 PB images at 64k cluster size
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
+ fprintf(stderr, "Active L1 table too large");
+ ret = -EFBIG;
+ goto fail;
+ }
s->l1_size = header.l1_size;
l1_vm_state_index = size_to_l1(s, header.size);
@@ -535,7 +542,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = -EINVAL;
goto fail;
}
+
+ ret = validate_table_offset(bs, header.l1_table_offset,
+ header.l1_size, sizeof(uint64_t));
+ if (ret < 0) {
+ fprintf(stderr, "Invalid L1 table offset");
+ goto fail;
+ }
s->l1_table_offset = header.l1_table_offset;
+
+
if (s->l1_size > 0) {
s->l1_table = g_malloc0(
align_offset(s->l1_size * sizeof(uint64_t), 512));

View File

@ -0,0 +1,50 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:47 +0100
Subject: [PATCH] qcow2: Fix backing file name length check
len could become negative and would pass the check then. Nothing bad
happened because bdrv_pread() happens to return an error for negative
length values, but make variables for sizes unsigned anyway.
This patch also changes the behaviour to error out on invalid lengths
instead of silently truncating it to 1023.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 6d33e8e7dc9d40ea105feed4b39caa3e641569e8)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 8dd285b..10bfaaf 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -344,7 +344,8 @@ static QemuOptsList qcow2_runtime_opts = {
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVQcowState *s = bs->opaque;
- int len, i, ret = 0;
+ unsigned int len, i;
+ int ret = 0;
QCowHeader header;
QemuOpts *opts;
Error *local_err = NULL;
@@ -593,8 +594,10 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
/* read the backing file name */
if (header.backing_file_offset != 0) {
len = header.backing_file_size;
- if (len > 1023) {
- len = 1023;
+ if (len > MIN(1023, s->cluster_size - header.backing_file_offset)) {
+ fprintf(stderr, "Backing file name too long");
+ ret = -EINVAL;
+ goto fail;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
bs->backing_file, len);

View File

@ -0,0 +1,46 @@
From: Hu Tao <hutao@cn.fujitsu.com>
Date: Sun, 26 Jan 2014 11:12:38 +0800
Subject: [PATCH] qcow2: fix offset overflow in qcow2_alloc_clusters_at()
When cluster size is big enough it can lead to an offset overflow
in qcow2_alloc_clusters_at(). This patch fixes it.
The allocation is stopped each time at L2 table boundary
(see handle_alloc()), so the possible maximum bytes could be
2^(cluster_bits - 3 + cluster_bits)
cluster_bits - 3 is used to compute the number of entry by L2
and the additional cluster_bits is to take into account each
clusters referenced by the L2 entries.
so int is safe for cluster_bits<=17, unsafe otherwise.
Signed-off-by: Hu Tao <hutao@cn.fujitsu.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Benoit Canet <benoit@irqsave.net>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 33304ec9fa484e765c6249673e09e1b7d49c5b85)
---
block/qcow2-refcount.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index cd641c9..1308151 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -676,7 +676,13 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
BDRVQcowState *s = bs->opaque;
uint64_t cluster_index;
uint64_t old_free_cluster_index;
- int i, refcount, ret;
+ uint64_t i;
+ int refcount, ret;
+
+ assert(nb_clusters >= 0);
+ if (nb_clusters == 0) {
+ return 0;
+ }
/* Check how many clusters there are free */
cluster_index = offset >> s->cluster_bits;

View File

@ -0,0 +1,224 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Fri, 28 Mar 2014 18:06:31 +0100
Subject: [PATCH] qcow2: Don't rely on free_cluster_index in
alloc_refcount_block() (CVE-2014-0147)
free_cluster_index is only correct if update_refcount() was called from
an allocation function, and even there it's brittle because it's used to
protect unfinished allocations which still have a refcount of 0 - if it
moves in the wrong place, the unfinished allocation can be corrupted.
So not using it any more seems to be a good idea. Instead, use the
first requested cluster to do the calculations. Return -EAGAIN if
unfinished allocations could become invalid and let the caller restart
its search for some free clusters.
The context of creating a snapsnot is one situation where
update_refcount() is called outside of a cluster allocation. For this
case, the change fixes a buffer overflow if a cluster is referenced in
an L2 table that cannot be represented by an existing refcount block.
(new_table[refcount_table_index] was out of bounds)
[Bump the qemu-iotests 026 refblock_alloc.write leak count from 10 to
11.
--Stefan]
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit b106ad9185f35fc4ad669555ad0e79e276083bd7)
Conflicts:
block/qcow2.c
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2-refcount.c | 72 ++++++++++++++++++++++++++------------------------
block/qcow2.c | 11 ++++----
2 files changed, 43 insertions(+), 40 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 1308151..d784dd6 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -191,10 +191,11 @@ static int alloc_refcount_block(BlockDriverState *bs,
* they can describe them themselves.
*
* - We need to consider that at this point we are inside update_refcounts
- * and doing the initial refcount increase. This means that some clusters
- * have already been allocated by the caller, but their refcount isn't
- * accurate yet. free_cluster_index tells us where this allocation ends
- * as long as we don't overwrite it by freeing clusters.
+ * and potentially doing an initial refcount increase. This means that
+ * some clusters have already been allocated by the caller, but their
+ * refcount isn't accurate yet. If we allocate clusters for metadata, we
+ * need to return -EAGAIN to signal the caller that it needs to restart
+ * the search for free clusters.
*
* - alloc_clusters_noref and qcow2_free_clusters may load a different
* refcount block into the cache
@@ -279,7 +280,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
}
s->refcount_table[refcount_table_index] = new_block;
- return 0;
+
+ /* The new refcount block may be where the caller intended to put its
+ * data, so let it restart the search. */
+ return -EAGAIN;
}
ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
@@ -302,8 +306,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
/* Calculate the number of refcount blocks needed so far */
uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT);
- uint64_t blocks_used = (s->free_cluster_index +
- refcount_block_clusters - 1) / refcount_block_clusters;
+ uint64_t blocks_used = DIV_ROUND_UP(cluster_index, refcount_block_clusters);
/* And now we need at least one block more for the new metadata */
uint64_t table_size = next_refcount_table_size(s, blocks_used + 1);
@@ -336,8 +339,6 @@ static int alloc_refcount_block(BlockDriverState *bs,
uint16_t *new_blocks = g_malloc0(blocks_clusters * s->cluster_size);
uint64_t *new_table = g_malloc0(table_size * sizeof(uint64_t));
- assert(meta_offset >= (s->free_cluster_index * s->cluster_size));
-
/* Fill the new refcount table */
memcpy(new_table, s->refcount_table,
s->refcount_table_size * sizeof(uint64_t));
@@ -400,18 +401,19 @@ static int alloc_refcount_block(BlockDriverState *bs,
s->refcount_table_size = table_size;
s->refcount_table_offset = table_offset;
- /* Free old table. Remember, we must not change free_cluster_index */
- uint64_t old_free_cluster_index = s->free_cluster_index;
+ /* Free old table. */
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t),
QCOW2_DISCARD_OTHER);
- s->free_cluster_index = old_free_cluster_index;
ret = load_refcount_block(bs, new_block, (void**) refcount_block);
if (ret < 0) {
return ret;
}
- return 0;
+ /* If we were trying to do the initial refcount update for some cluster
+ * allocation, we might have used the same clusters to store newly
+ * allocated metadata. Make the caller search some new space. */
+ return -EAGAIN;
fail_table:
g_free(new_table);
@@ -657,12 +659,15 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
- offset = alloc_clusters_noref(bs, size);
- if (offset < 0) {
- return offset;
- }
+ do {
+ offset = alloc_clusters_noref(bs, size);
+ if (offset < 0) {
+ return offset;
+ }
+
+ ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER);
+ } while (ret == -EAGAIN);
- ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER);
if (ret < 0) {
return ret;
}
@@ -675,7 +680,6 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
{
BDRVQcowState *s = bs->opaque;
uint64_t cluster_index;
- uint64_t old_free_cluster_index;
uint64_t i;
int refcount, ret;
@@ -684,30 +688,28 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
return 0;
}
- /* Check how many clusters there are free */
- cluster_index = offset >> s->cluster_bits;
- for(i = 0; i < nb_clusters; i++) {
- refcount = get_refcount(bs, cluster_index++);
+ do {
+ /* Check how many clusters there are free */
+ cluster_index = offset >> s->cluster_bits;
+ for(i = 0; i < nb_clusters; i++) {
+ refcount = get_refcount(bs, cluster_index++);
- if (refcount < 0) {
- return refcount;
- } else if (refcount != 0) {
- break;
+ if (refcount < 0) {
+ return refcount;
+ } else if (refcount != 0) {
+ break;
+ }
}
- }
- /* And then allocate them */
- old_free_cluster_index = s->free_cluster_index;
- s->free_cluster_index = cluster_index + i;
+ /* And then allocate them */
+ ret = update_refcount(bs, offset, i << s->cluster_bits, 1,
+ QCOW2_DISCARD_NEVER);
+ } while (ret == -EAGAIN);
- ret = update_refcount(bs, offset, i << s->cluster_bits, 1,
- QCOW2_DISCARD_NEVER);
if (ret < 0) {
return ret;
}
- s->free_cluster_index = old_free_cluster_index;
-
return i;
}
diff --git a/block/qcow2.c b/block/qcow2.c
index 10bfaaf..5d45036 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1385,7 +1385,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
*/
BlockDriverState* bs;
QCowHeader *header;
- uint8_t* refcount_table;
+ uint64_t* refcount_table;
int ret;
ret = bdrv_create_file(filename, options);
@@ -1431,9 +1431,10 @@ static int qcow2_create2(const char *filename, int64_t total_size,
goto out;
}
- /* Write an empty refcount table */
- refcount_table = g_malloc0(cluster_size);
- ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
+ /* Write a refcount table with one refcount block */
+ refcount_table = g_malloc0(2 * cluster_size);
+ refcount_table[0] = cpu_to_be64(2 * cluster_size);
+ ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size);
g_free(refcount_table);
if (ret < 0) {
@@ -1455,7 +1456,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
goto out;
}
- ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
+ ret = qcow2_alloc_clusters(bs, 3 * cluster_size);
if (ret < 0) {
goto out;

View File

@ -0,0 +1,28 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:49 +0100
Subject: [PATCH] qcow2: Avoid integer overflow in get_refcount (CVE-2014-0143)
This ensures that the checks catch all invalid cluster indexes
instead of returning the refcount of a wrong cluster.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit db8a31d11d6a60f48d6817530640d75aa72a9a2f)
---
block/qcow2-refcount.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index d784dd6..3e473bd 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -87,7 +87,7 @@ static int load_refcount_block(BlockDriverState *bs,
static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
{
BDRVQcowState *s = bs->opaque;
- int refcount_table_index, block_index;
+ uint64_t refcount_table_index, block_index;
int64_t refcount_block_offset;
int ret;
uint16_t *refcount_block;

View File

@ -0,0 +1,77 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:50 +0100
Subject: [PATCH] qcow2: Check new refcount table size on growth
If the size becomes larger than what qcow2_open() would accept, fail the
growing operation.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 2b5d5953eec0cc541857c3df812bdf8421596ab2)
Conflicts:
block/qcow2.c
---
block/qcow2-refcount.c | 4 ++++
block/qcow2.c | 4 +---
block/qcow2.h | 9 +++++++++
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 3e473bd..3cdcfb6 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -308,6 +308,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT);
uint64_t blocks_used = DIV_ROUND_UP(cluster_index, refcount_block_clusters);
+ if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
+ return -EFBIG;
+ }
+
/* And now we need at least one block more for the new metadata */
uint64_t table_size = next_refcount_table_size(s, blocks_used + 1);
uint64_t last_table_size;
diff --git a/block/qcow2.c b/block/qcow2.c
index 5d45036..af0a45c 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -487,9 +487,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
s->refcount_table_size =
header.refcount_table_clusters << (s->cluster_bits - 3);
- if (header.refcount_table_clusters > (0x800000 >> s->cluster_bits)) {
- /* 8 MB refcount table is enough for 2 PB images at 64k cluster size
- * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
+ if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
fprintf(stderr, "Reference count table too large");
ret = -EINVAL;
goto fail;
diff --git a/block/qcow2.h b/block/qcow2.h
index 1e1294c..e802c55 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -40,6 +40,10 @@
#define QCOW_MAX_CRYPT_CLUSTERS 32
#define QCOW_MAX_SNAPSHOTS 65536
+/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
+#define QCOW_MAX_REFTABLE_SIZE 0x800000
+
/* indicate that the refcount of the referenced cluster is exactly one. */
#define QCOW_OFLAG_COPIED (1LL << 63)
/* indicate that the cluster is compressed (they never have the copied flag) */
@@ -356,6 +360,11 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
}
+static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s)
+{
+ return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
+}
+
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
{
if (l2_entry & QCOW_OFLAG_COMPRESSED) {

View File

@ -0,0 +1,85 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:51 +0100
Subject: [PATCH] qcow2: Fix types in qcow2_alloc_clusters and
alloc_clusters_noref
In order to avoid integer overflows.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit bb572aefbdac290363bfa5ca0e810ccce0a14ed6)
Conflicts:
block/qcow2.h
---
block/qcow2-refcount.c | 11 ++++++-----
block/qcow2.h | 6 +++---
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 3cdcfb6..d424c22 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -26,7 +26,7 @@
#include "block/block_int.h"
#include "block/qcow2.h"
-static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
+static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
int64_t offset, int64_t length,
int addend, enum qcow2_discard_type type);
@@ -632,15 +632,16 @@ static int update_cluster_refcount(BlockDriverState *bs,
/* return < 0 if error */
-static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size)
+static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
{
BDRVQcowState *s = bs->opaque;
- int i, nb_clusters, refcount;
+ uint64_t i, nb_clusters;
+ int refcount;
nb_clusters = size_to_clusters(s, size);
retry:
for(i = 0; i < nb_clusters; i++) {
- int64_t next_cluster_index = s->free_cluster_index++;
+ uint64_t next_cluster_index = s->free_cluster_index++;
refcount = get_refcount(bs, next_cluster_index);
if (refcount < 0) {
@@ -657,7 +658,7 @@ retry:
return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
}
-int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
+int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
{
int64_t offset;
int ret;
diff --git a/block/qcow2.h b/block/qcow2.h
index e802c55..baf62a0 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -210,8 +210,8 @@ typedef struct BDRVQcowState {
uint64_t *refcount_table;
uint64_t refcount_table_offset;
uint32_t refcount_table_size;
- int64_t free_cluster_index;
- int64_t free_byte_offset;
+ uint64_t free_cluster_index;
+ uint64_t free_byte_offset;
CoMutex lock;
@@ -408,7 +408,7 @@ int qcow2_update_header(BlockDriverState *bs);
int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
-int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
+int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
int nb_clusters);
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);

View File

@ -0,0 +1,61 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:52 +0100
Subject: [PATCH] qcow2: Protect against some integer overflows in bdrv_check
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 0abe740f1de899737242bcba1fb4a9857f7a3087)
---
block/qcow2-refcount.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index d424c22..fc2d367 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -997,8 +997,7 @@ static void inc_refcounts(BlockDriverState *bs,
int64_t offset, int64_t size)
{
BDRVQcowState *s = bs->opaque;
- int64_t start, last, cluster_offset;
- int k;
+ uint64_t start, last, cluster_offset, k;
if (size <= 0)
return;
@@ -1008,11 +1007,7 @@ static void inc_refcounts(BlockDriverState *bs,
for(cluster_offset = start; cluster_offset <= last;
cluster_offset += s->cluster_size) {
k = cluster_offset >> s->cluster_bits;
- if (k < 0) {
- fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n",
- cluster_offset);
- res->corruptions++;
- } else if (k >= refcount_table_size) {
+ if (k >= refcount_table_size) {
fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after "
"the end of the image file, can't properly check refcounts.\n",
cluster_offset);
@@ -1253,14 +1248,19 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
BdrvCheckMode fix)
{
BDRVQcowState *s = bs->opaque;
- int64_t size, i, highest_cluster;
- int nb_clusters, refcount1, refcount2;
+ int64_t size, i, highest_cluster, nb_clusters;
+ int refcount1, refcount2;
QCowSnapshot *sn;
uint16_t *refcount_table;
int ret;
size = bdrv_getlength(bs->file);
nb_clusters = size_to_clusters(s, size);
+ if (nb_clusters > INT_MAX) {
+ res->check_errors++;
+ return -EFBIG;
+ }
+
refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
res->bfi.total_clusters =

View File

@ -0,0 +1,28 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:05:53 +0100
Subject: [PATCH] qcow2: Fix new L1 table size check (CVE-2014-0143)
The size in bytes is assigned to an int later, so check that instead of
the number of entries.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit cab60de930684c33f67d4e32c7509b567f8c445b)
---
block/qcow2-cluster.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 09abbf0..06e3e14 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -54,7 +54,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
}
}
- if (new_l1_size > INT_MAX) {
+ if (new_l1_size > INT_MAX / sizeof(uint64_t)) {
return -EFBIG;
}

View File

@ -0,0 +1,321 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:54 +0100
Subject: [PATCH] dmg: coding style and indentation cleanup
Clean up the mix of tabs and spaces, as well as the coding style
violations in block/dmg.c. There are no semantic changes since this
patch simply reformats the code.
This patch is necessary before we can make meaningful changes to this
file, due to the inconsistent formatting and confusing indentation.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 2c1885adcf0312da80c7317b09f9adad97fa0fc6)
---
block/dmg.c | 224 ++++++++++++++++++++++++++++++++----------------------------
1 file changed, 120 insertions(+), 104 deletions(-)
diff --git a/block/dmg.c b/block/dmg.c
index 3141cb5..e8b88dc 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -95,9 +95,9 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVDMGState *s = bs->opaque;
- uint64_t info_begin,info_end,last_in_offset,last_out_offset;
+ uint64_t info_begin, info_end, last_in_offset, last_out_offset;
uint32_t count, tmp;
- uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
+ uint32_t max_compressed_size = 1, max_sectors_per_chunk = 1, i;
int64_t offset;
int ret;
@@ -159,37 +159,39 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
- if (type == 0x6d697368 && count >= 244) {
- int new_size, chunk_count;
+ if (type == 0x6d697368 && count >= 244) {
+ int new_size, chunk_count;
offset += 4;
offset += 200;
- chunk_count = (count-204)/40;
- new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
- s->types = g_realloc(s->types, new_size/2);
- s->offsets = g_realloc(s->offsets, new_size);
- s->lengths = g_realloc(s->lengths, new_size);
- s->sectors = g_realloc(s->sectors, new_size);
- s->sectorcounts = g_realloc(s->sectorcounts, new_size);
+ chunk_count = (count - 204) / 40;
+ new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
+ s->types = g_realloc(s->types, new_size / 2);
+ s->offsets = g_realloc(s->offsets, new_size);
+ s->lengths = g_realloc(s->lengths, new_size);
+ s->sectors = g_realloc(s->sectors, new_size);
+ s->sectorcounts = g_realloc(s->sectorcounts, new_size);
for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) {
ret = read_uint32(bs, offset, &s->types[i]);
if (ret < 0) {
goto fail;
}
- offset += 4;
- if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
- if(s->types[i]==0xffffffff) {
- last_in_offset = s->offsets[i-1]+s->lengths[i-1];
- last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
- }
- chunk_count--;
- i--;
- offset += 36;
- continue;
- }
- offset += 4;
+ offset += 4;
+ if (s->types[i] != 0x80000005 && s->types[i] != 1 &&
+ s->types[i] != 2) {
+ if (s->types[i] == 0xffffffff) {
+ last_in_offset = s->offsets[i - 1] + s->lengths[i - 1];
+ last_out_offset = s->sectors[i - 1] +
+ s->sectorcounts[i - 1];
+ }
+ chunk_count--;
+ i--;
+ offset += 36;
+ continue;
+ }
+ offset += 4;
ret = read_uint64(bs, offset, &s->sectors[i]);
if (ret < 0) {
@@ -217,19 +219,21 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
}
offset += 8;
- if(s->lengths[i]>max_compressed_size)
- max_compressed_size = s->lengths[i];
- if(s->sectorcounts[i]>max_sectors_per_chunk)
- max_sectors_per_chunk = s->sectorcounts[i];
- }
- s->n_chunks+=chunk_count;
- }
+ if (s->lengths[i] > max_compressed_size) {
+ max_compressed_size = s->lengths[i];
+ }
+ if (s->sectorcounts[i] > max_sectors_per_chunk) {
+ max_sectors_per_chunk = s->sectorcounts[i];
+ }
+ }
+ s->n_chunks += chunk_count;
+ }
}
/* initialize zlib engine */
- s->compressed_chunk = g_malloc(max_compressed_size+1);
- s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk);
- if(inflateInit(&s->zstream) != Z_OK) {
+ s->compressed_chunk = g_malloc(max_compressed_size + 1);
+ s->uncompressed_chunk = g_malloc(512 * max_sectors_per_chunk);
+ if (inflateInit(&s->zstream) != Z_OK) {
ret = -EINVAL;
goto fail;
}
@@ -251,27 +255,29 @@ fail:
}
static inline int is_sector_in_chunk(BDRVDMGState* s,
- uint32_t chunk_num,int sector_num)
+ uint32_t chunk_num, int sector_num)
{
- if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
- s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
- return 0;
- else
- return -1;
+ if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num ||
+ s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) {
+ return 0;
+ } else {
+ return -1;
+ }
}
-static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
+static inline uint32_t search_chunk(BDRVDMGState *s, int sector_num)
{
/* binary search */
- uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
- while(chunk1!=chunk2) {
- chunk3 = (chunk1+chunk2)/2;
- if(s->sectors[chunk3]>sector_num)
- chunk2 = chunk3;
- else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
- return chunk3;
- else
- chunk1 = chunk3;
+ uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
+ while (chunk1 != chunk2) {
+ chunk3 = (chunk1 + chunk2) / 2;
+ if (s->sectors[chunk3] > sector_num) {
+ chunk2 = chunk3;
+ } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
+ return chunk3;
+ } else {
+ chunk1 = chunk3;
+ }
}
return s->n_chunks; /* error */
}
@@ -280,54 +286,62 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
{
BDRVDMGState *s = bs->opaque;
- if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
- int ret;
- uint32_t chunk = search_chunk(s,sector_num);
+ if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) {
+ int ret;
+ uint32_t chunk = search_chunk(s, sector_num);
- if(chunk>=s->n_chunks)
- return -1;
+ if (chunk >= s->n_chunks) {
+ return -1;
+ }
- s->current_chunk = s->n_chunks;
- switch(s->types[chunk]) {
- case 0x80000005: { /* zlib compressed */
- int i;
+ s->current_chunk = s->n_chunks;
+ switch (s->types[chunk]) {
+ case 0x80000005: { /* zlib compressed */
+ int i;
- /* we need to buffer, because only the chunk as whole can be
- * inflated. */
- i=0;
- do {
+ /* we need to buffer, because only the chunk as whole can be
+ * inflated. */
+ i = 0;
+ do {
ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
- s->compressed_chunk+i, s->lengths[chunk]-i);
- if(ret<0 && errno==EINTR)
- ret=0;
- i+=ret;
- } while(ret>=0 && ret+i<s->lengths[chunk]);
-
- if (ret != s->lengths[chunk])
- return -1;
-
- s->zstream.next_in = s->compressed_chunk;
- s->zstream.avail_in = s->lengths[chunk];
- s->zstream.next_out = s->uncompressed_chunk;
- s->zstream.avail_out = 512*s->sectorcounts[chunk];
- ret = inflateReset(&s->zstream);
- if(ret != Z_OK)
- return -1;
- ret = inflate(&s->zstream, Z_FINISH);
- if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
- return -1;
- break; }
- case 1: /* copy */
- ret = bdrv_pread(bs->file, s->offsets[chunk],
+ s->compressed_chunk + i,
+ s->lengths[chunk] - i);
+ if (ret < 0 && errno == EINTR) {
+ ret = 0;
+ }
+ i += ret;
+ } while (ret >= 0 && ret + i < s->lengths[chunk]);
+
+ if (ret != s->lengths[chunk]) {
+ return -1;
+ }
+
+ s->zstream.next_in = s->compressed_chunk;
+ s->zstream.avail_in = s->lengths[chunk];
+ s->zstream.next_out = s->uncompressed_chunk;
+ s->zstream.avail_out = 512 * s->sectorcounts[chunk];
+ ret = inflateReset(&s->zstream);
+ if (ret != Z_OK) {
+ return -1;
+ }
+ ret = inflate(&s->zstream, Z_FINISH);
+ if (ret != Z_STREAM_END ||
+ s->zstream.total_out != 512 * s->sectorcounts[chunk]) {
+ return -1;
+ }
+ break; }
+ case 1: /* copy */
+ ret = bdrv_pread(bs->file, s->offsets[chunk],
s->uncompressed_chunk, s->lengths[chunk]);
- if (ret != s->lengths[chunk])
- return -1;
- break;
- case 2: /* zero */
- memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
- break;
- }
- s->current_chunk = chunk;
+ if (ret != s->lengths[chunk]) {
+ return -1;
+ }
+ break;
+ case 2: /* zero */
+ memset(s->uncompressed_chunk, 0, 512 * s->sectorcounts[chunk]);
+ break;
+ }
+ s->current_chunk = chunk;
}
return 0;
}
@@ -338,12 +352,14 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num,
BDRVDMGState *s = bs->opaque;
int i;
- for(i=0;i<nb_sectors;i++) {
- uint32_t sector_offset_in_chunk;
- if(dmg_read_chunk(bs, sector_num+i) != 0)
- return -1;
- sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
- memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
+ for (i = 0; i < nb_sectors; i++) {
+ uint32_t sector_offset_in_chunk;
+ if (dmg_read_chunk(bs, sector_num + i) != 0) {
+ return -1;
+ }
+ sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk];
+ memcpy(buf + i * 512,
+ s->uncompressed_chunk + sector_offset_in_chunk * 512, 512);
}
return 0;
}
@@ -375,12 +391,12 @@ static void dmg_close(BlockDriverState *bs)
}
static BlockDriver bdrv_dmg = {
- .format_name = "dmg",
- .instance_size = sizeof(BDRVDMGState),
- .bdrv_probe = dmg_probe,
- .bdrv_open = dmg_open,
- .bdrv_read = dmg_co_read,
- .bdrv_close = dmg_close,
+ .format_name = "dmg",
+ .instance_size = sizeof(BDRVDMGState),
+ .bdrv_probe = dmg_probe,
+ .bdrv_open = dmg_open,
+ .bdrv_read = dmg_co_read,
+ .bdrv_close = dmg_close,
};
static void bdrv_dmg_init(void)

View File

@ -0,0 +1,33 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:55 +0100
Subject: [PATCH] dmg: prevent out-of-bounds array access on terminator
When a terminator is reached the base for offsets and sectors is stored.
The following records that are processed will use this base value.
If the first record we encounter is a terminator, then calculating the
base values would result in out-of-bounds array accesses. Don't do
that.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 73ed27ec28a1dbebdd2ae792284151f029950fbe)
---
block/dmg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/block/dmg.c b/block/dmg.c
index e8b88dc..cb4060c 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -181,7 +181,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
offset += 4;
if (s->types[i] != 0x80000005 && s->types[i] != 1 &&
s->types[i] != 2) {
- if (s->types[i] == 0xffffffff) {
+ if (s->types[i] == 0xffffffff && i > 0) {
last_in_offset = s->offsets[i - 1] + s->lengths[i - 1];
last_out_offset = s->sectors[i - 1] +
s->sectorcounts[i - 1];

View File

@ -0,0 +1,56 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:56 +0100
Subject: [PATCH] dmg: drop broken bdrv_pread() loop
It is not necessary to check errno for EINTR and the block layer does
not produce short reads. Therefore we can drop the loop that attempts
to read a compressed chunk.
The loop is buggy because it incorrectly adds the transferred bytes
twice:
do {
ret = bdrv_pread(...);
i += ret;
} while (ret >= 0 && ret + i < s->lengths[chunk]);
Luckily we can drop the loop completely and perform a single
bdrv_pread().
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit b404bf854217dbe8a5649449eb3ad33777f7d900)
---
block/dmg.c | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/block/dmg.c b/block/dmg.c
index cb4060c..24f08ef 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -297,21 +297,10 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
s->current_chunk = s->n_chunks;
switch (s->types[chunk]) {
case 0x80000005: { /* zlib compressed */
- int i;
-
/* we need to buffer, because only the chunk as whole can be
* inflated. */
- i = 0;
- do {
- ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
- s->compressed_chunk + i,
- s->lengths[chunk] - i);
- if (ret < 0 && errno == EINTR) {
- ret = 0;
- }
- i += ret;
- } while (ret >= 0 && ret + i < s->lengths[chunk]);
-
+ ret = bdrv_pread(bs->file, s->offsets[chunk],
+ s->compressed_chunk, s->lengths[chunk]);
if (ret != s->lengths[chunk]) {
return -1;
}

View File

@ -0,0 +1,41 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:57 +0100
Subject: [PATCH] dmg: use appropriate types when reading chunks
Use the right types instead of signed int:
size_t new_size;
This is a byte count for g_realloc() that is calculated from uint32_t
and size_t values.
uint32_t chunk_count;
Use the same type as s->n_chunks, which is used together with
chunk_count.
This patch is a cleanup and does not fix bugs.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit eb71803b041f55779ea10d860c0f66df285c68de)
---
block/dmg.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/block/dmg.c b/block/dmg.c
index 24f08ef..5650e73 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -160,7 +160,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
}
if (type == 0x6d697368 && count >= 244) {
- int new_size, chunk_count;
+ size_t new_size;
+ uint32_t chunk_count;
offset += 4;
offset += 200;

View File

@ -0,0 +1,67 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:58 +0100
Subject: [PATCH] dmg: sanitize chunk length and sectorcount (CVE-2014-0145)
Chunk length and sectorcount are used for decompression buffers as well
as the bdrv_pread() count argument. Ensure that they have reasonable
values so neither memory allocation nor conversion from uint64_t to int
will cause problems.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit c165f7758009a4f793c1fc19ebb69cf55313450b)
---
block/dmg.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/block/dmg.c b/block/dmg.c
index 5650e73..1a751ea 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -27,6 +27,14 @@
#include "qemu/module.h"
#include <zlib.h>
+enum {
+ /* Limit chunk sizes to prevent unreasonable amounts of memory being used
+ * or truncating when converting to 32-bit types
+ */
+ DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */
+ DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
+};
+
typedef struct BDRVDMGState {
CoMutex lock;
/* each chunk contains a certain number of sectors,
@@ -207,6 +215,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
}
offset += 8;
+ if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
+ error_report("sector count %" PRIu64 " for chunk %u is "
+ "larger than max (%u)",
+ s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
+ ret = -EINVAL;
+ goto fail;
+ }
+
ret = read_uint64(bs, offset, &s->offsets[i]);
if (ret < 0) {
goto fail;
@@ -220,6 +236,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
}
offset += 8;
+ if (s->lengths[i] > DMG_LENGTHS_MAX) {
+ error_report("length %" PRIu64 " for chunk %u is larger "
+ "than max (%u)",
+ s->lengths[i], i, DMG_LENGTHS_MAX);
+ ret = -EINVAL;
+ goto fail;
+ }
+
if (s->lengths[i] > max_compressed_size) {
max_compressed_size = s->lengths[i];
}

View File

@ -0,0 +1,48 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:05:59 +0100
Subject: [PATCH] dmg: use uint64_t consistently for sectors and lengths
The DMG metadata is stored as uint64_t, so use the same type for
sector_num. int was a particularly poor choice since it is only 32-bit
and would truncate large values.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 686d7148ec23402a172628c800022b3a95a022c9)
---
block/dmg.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/block/dmg.c b/block/dmg.c
index 1a751ea..5229876 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -280,7 +280,7 @@ fail:
}
static inline int is_sector_in_chunk(BDRVDMGState* s,
- uint32_t chunk_num, int sector_num)
+ uint32_t chunk_num, uint64_t sector_num)
{
if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num ||
s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) {
@@ -290,7 +290,7 @@ static inline int is_sector_in_chunk(BDRVDMGState* s,
}
}
-static inline uint32_t search_chunk(BDRVDMGState *s, int sector_num)
+static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num)
{
/* binary search */
uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
@@ -307,7 +307,7 @@ static inline uint32_t search_chunk(BDRVDMGState *s, int sector_num)
return s->n_chunks; /* error */
}
-static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
+static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
{
BDRVDMGState *s = bs->opaque;

View File

@ -0,0 +1,94 @@
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 26 Mar 2014 13:06:00 +0100
Subject: [PATCH] dmg: prevent chunk buffer overflow (CVE-2014-0145)
Both compressed and uncompressed I/O is buffered. dmg_open() calculates
the maximum buffer size needed from the metadata in the image file.
There is currently a buffer overflow since ->lengths[] is accounted
against the maximum compressed buffer size but actually uses the
uncompressed buffer:
switch (s->types[chunk]) {
case 1: /* copy */
ret = bdrv_pread(bs->file, s->offsets[chunk],
s->uncompressed_chunk, s->lengths[chunk]);
We must account against the maximum uncompressed buffer size for type=1
chunks.
This patch fixes the maximum buffer size calculation to take into
account the chunk type. It is critical that we update the correct
maximum since there are two buffers ->compressed_chunk and
->uncompressed_chunk.
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit f0dce23475b5af5da6b17b97c1765271307734b6)
Conflicts:
block/dmg.c
---
block/dmg.c | 40 ++++++++++++++++++++++++++++++++++------
1 file changed, 34 insertions(+), 6 deletions(-)
diff --git a/block/dmg.c b/block/dmg.c
index 5229876..8d5d42f 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -100,6 +100,38 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
return 0;
}
+static int dmg_open(BlockDriverState *bs, QDict *options, int flags);
+/* Increase max chunk sizes, if necessary. This function is used to calculate
+ * the buffer sizes needed for compressed/uncompressed chunk I/O.
+ */
+static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk,
+ uint32_t *max_compressed_size,
+ uint32_t *max_sectors_per_chunk)
+{
+ uint32_t compressed_size = 0;
+ uint32_t uncompressed_sectors = 0;
+
+ switch (s->types[chunk]) {
+ case 0x80000005: /* zlib compressed */
+ compressed_size = s->lengths[chunk];
+ uncompressed_sectors = s->sectorcounts[chunk];
+ break;
+ case 1: /* copy */
+ uncompressed_sectors = (s->lengths[chunk] + 511) / 512;
+ break;
+ case 2: /* zero */
+ uncompressed_sectors = s->sectorcounts[chunk];
+ break;
+ }
+
+ if (compressed_size > *max_compressed_size) {
+ *max_compressed_size = compressed_size;
+ }
+ if (uncompressed_sectors > *max_sectors_per_chunk) {
+ *max_sectors_per_chunk = uncompressed_sectors;
+ }
+}
+
static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
{
BDRVDMGState *s = bs->opaque;
@@ -244,12 +276,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
- if (s->lengths[i] > max_compressed_size) {
- max_compressed_size = s->lengths[i];
- }
- if (s->sectorcounts[i] > max_sectors_per_chunk) {
- max_sectors_per_chunk = s->sectorcounts[i];
- }
+ update_max_chunk_size(s, i, &max_compressed_size,
+ &max_sectors_per_chunk);
}
s->n_chunks += chunk_count;
}

View File

@ -0,0 +1,32 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:02 +0100
Subject: [PATCH] block: Limit request size (CVE-2014-0143)
Limiting the size of a single request to INT_MAX not only fixes a
direct integer overflow in bdrv_check_request() (which would only
trigger bad behaviour with ridiculously huge images, as in close to
2^64 bytes), but can also prevent overflows in all block drivers.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 8f4754ede56e3f9ea3fd7207f4a7c4453e59285b)
---
block.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/block.c b/block.c
index 8ce8b91..6c48469 100644
--- a/block.c
+++ b/block.c
@@ -2160,6 +2160,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
int nb_sectors)
{
+ if (nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) {
+ return -EIO;
+ }
+
return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE);
}

View File

@ -0,0 +1,117 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:03 +0100
Subject: [PATCH] qcow2: Fix copy_sectors() with VM state
bs->total_sectors is not the highest possible sector number that could
be involved in a copy on write operation: VM state is after the end of
the virtual disk. This resulted in wrong values for the number of
sectors to be copied (n).
The code that checks for the end of the image isn't required any more
because the code hasn't been calling the block layer's bdrv_read() for a
long time; instead, it directly calls qcow2_readv(), which doesn't error
out on VM state sector numbers.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 6b7d4c55586a849aa8313282d79432917eade3bf)
---
block/qcow2-cluster.c | 9 ---------
tests/qemu-iotests/029 | 22 ++++++++++++++++++++--
tests/qemu-iotests/029.out | 13 +++++++++++++
3 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 06e3e14..3df140c 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -335,15 +335,6 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
struct iovec iov;
int n, ret;
- /*
- * If this is the last cluster and it is only partially used, we must only
- * copy until the end of the image, or bdrv_check_request will fail for the
- * bdrv_read/write calls below.
- */
- if (start_sect + n_end > bs->total_sectors) {
- n_end = bs->total_sectors - start_sect;
- }
-
n = n_end - n_start;
if (n <= 0) {
return 0;
diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029
index 0ad5e45..478fd96 100755
--- a/tests/qemu-iotests/029
+++ b/tests/qemu-iotests/029
@@ -1,7 +1,6 @@
#!/bin/bash
#
-# Test loading internal snapshots where the L1 table of the snapshot
-# is smaller than the current L1 table.
+# qcow2 internal snapshots/VM state tests
#
# Copyright (C) 2011 Red Hat, Inc.
#
@@ -45,6 +44,11 @@ _supported_fmt qcow2
_supported_proto generic
_supported_os Linux
+echo
+echo Test loading internal snapshots where the L1 table of the snapshot
+echo is smaller than the current L1 table.
+echo
+
CLUSTER_SIZE=65536
_make_test_img 64M
$QEMU_IMG snapshot -c foo $TEST_IMG
@@ -59,6 +63,20 @@ $QEMU_IO -c 'write -b 0 4M' $TEST_IMG | _filter_qemu_io
$QEMU_IMG snapshot -a foo $TEST_IMG
_check_test_img
+
+echo
+echo Try using a huge VM state
+echo
+
+CLUSTER_SIZE=65536
+_make_test_img 64M
+{ $QEMU_IO -c "write -b -P 0x11 1T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IMG snapshot -a foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+{ $QEMU_IO -c "read -b -P 0x11 1T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+_check_test_img
+
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out
index 0eedb3a..9029698 100644
--- a/tests/qemu-iotests/029.out
+++ b/tests/qemu-iotests/029.out
@@ -1,4 +1,8 @@
QA output created by 029
+
+Test loading internal snapshots where the L1 table of the snapshot
+is smaller than the current L1 table.
+
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 4096/4096 bytes at offset 0
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -7,4 +11,13 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216
wrote 4194304/4194304 bytes at offset 0
4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
+
+Try using a huge VM state
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+wrote 4096/4096 bytes at offset 1099511627776
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 1099511627776
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
*** done

View File

@ -0,0 +1,48 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:04 +0100
Subject: [PATCH] qcow2: Fix NULL dereference in qcow2_open() error path
(CVE-2014-0146)
The qcow2 code assumes that s->snapshots is non-NULL if s->nb_snapshots
!= 0. By having the initialisation of both fields separated in
qcow2_open(), any error occuring in between would cause the error path
to dereference NULL in qcow2_free_snapshots() if the image had any
snapshots.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 11b128f4062dd7f89b14abc8877ff20d41b28be9)
Conflicts:
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index af0a45c..c9beb01 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -515,9 +515,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
goto fail;
}
- s->snapshots_offset = header.snapshots_offset;
- s->nb_snapshots = header.nb_snapshots;
-
/* read the level 1 table */
if (header.l1_size > 0x2000000) {
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
@@ -605,6 +602,10 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
bs->backing_file[len] = '\0';
}
+ /* Internal snapshots */
+ s->snapshots_offset = header.snapshots_offset;
+ s->nb_snapshots = header.nb_snapshots;
+
ret = qcow2_read_snapshots(bs);
if (ret < 0) {
goto fail;

View File

@ -0,0 +1,89 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:05 +0100
Subject: [PATCH] qcow2: Fix L1 allocation size in qcow2_snapshot_load_tmp()
(CVE-2014-0145)
For the L1 table to loaded for an internal snapshot, the code allocated
only enough memory to hold the currently active L1 table. If the
snapshot's L1 table is actually larger than the current one, this leads
to a buffer overflow.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit c05e4667be91b46ab42b5a11babf8e84d476cc6b)
---
block/qcow2-snapshot.c | 2 +-
tests/qemu-iotests/029 | 18 +++++++++++++++++-
tests/qemu-iotests/029.out | 4 ++++
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index eb80438..dc8736a 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -622,7 +622,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
sn = &s->snapshots[snapshot_index];
/* Allocate and read in the snapshot's L1 table */
- new_l1_bytes = s->l1_size * sizeof(uint64_t);
+ new_l1_bytes = sn->l1_size * sizeof(uint64_t);
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029
index 478fd96..dfb5726 100755
--- a/tests/qemu-iotests/029
+++ b/tests/qemu-iotests/029
@@ -30,7 +30,8 @@ status=1 # failure is the default!
_cleanup()
{
- _cleanup_test_img
+ rm -f $TEST_IMG.snap
+ _cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -44,6 +45,9 @@ _supported_fmt qcow2
_supported_proto generic
_supported_os Linux
+offset_size=24
+offset_l1_size=36
+
echo
echo Test loading internal snapshots where the L1 table of the snapshot
echo is smaller than the current L1 table.
@@ -77,6 +81,18 @@ _make_test_img 64M
_check_test_img
+echo
+echo "qcow2_snapshot_load_tmp() should take the L1 size from the snapshot"
+echo
+
+CLUSTER_SIZE=512
+_make_test_img 64M
+{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
+poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00"
+poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01"
+{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir
+
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out
index 9029698..ce0e64d 100644
--- a/tests/qemu-iotests/029.out
+++ b/tests/qemu-iotests/029.out
@@ -20,4 +20,8 @@ wrote 4096/4096 bytes at offset 1099511627776
read 4096/4096 bytes at offset 1099511627776
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
+
+qcow2_snapshot_load_tmp() should take the L1 size from the snapshot
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
*** done

View File

@ -0,0 +1,67 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:06 +0100
Subject: [PATCH] qcow2: Check maximum L1 size in qcow2_snapshot_load_tmp()
(CVE-2014-0143)
This avoids an unbounded allocation.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 6a83f8b5bec6f59e56cc49bd49e4c3f8f805d56f)
Conflicts:
block/qcow2.c
tests/qemu-iotests/080
tests/qemu-iotests/080.out
---
block/qcow2-snapshot.c | 4 ++++
block/qcow2.c | 4 +---
block/qcow2.h | 4 ++++
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index dc8736a..d2c956c 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -622,6 +622,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
sn = &s->snapshots[snapshot_index];
/* Allocate and read in the snapshot's L1 table */
+ if (sn->l1_size > QCOW_MAX_L1_SIZE) {
+ fprintf(stderr, "Snapshot L1 table too large");
+ return -EFBIG;
+ }
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
diff --git a/block/qcow2.c b/block/qcow2.c
index c9beb01..5dfd5ec 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -516,9 +516,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
}
/* read the level 1 table */
- if (header.l1_size > 0x2000000) {
- /* 32 MB L1 table is enough for 2 PB images at 64k cluster size
- * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
+ if (header.l1_size > QCOW_MAX_L1_SIZE) {
fprintf(stderr, "Active L1 table too large");
ret = -EFBIG;
goto fail;
diff --git a/block/qcow2.h b/block/qcow2.h
index baf62a0..0912488 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -44,6 +44,10 @@
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
#define QCOW_MAX_REFTABLE_SIZE 0x800000
+/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
+#define QCOW_MAX_L1_SIZE 0x2000000
+
/* indicate that the refcount of the referenced cluster is exactly one. */
#define QCOW_OFLAG_COPIED (1LL << 63)
/* indicate that the cluster is compressed (they never have the copied flag) */

View File

@ -0,0 +1,81 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:07 +0100
Subject: [PATCH] qcow2: Limit snapshot table size
Even with a limit of 64k snapshots, each snapshot could have a filename
and an ID with up to 64k, which would still lead to pretty large
allocations, which could potentially lead to qemu aborting. Limit the
total size of the snapshot table to an average of 1k per entry when
the limit of 64k snapshots is fully used. This should be plenty for any
reasonable user.
This also fixes potential integer overflows of s->snapshot_size.
Suggested-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 5dae6e30c531feb31eed99f9039b52bf70832ce3)
---
block/qcow2-snapshot.c | 15 ++++++++++++++-
block/qcow2.h | 4 ++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index d2c956c..5d3c0e6 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -116,8 +116,14 @@ int qcow2_read_snapshots(BlockDriverState *bs)
}
offset += name_size;
sn->name[name_size] = '\0';
+
+ if (offset - s->snapshots_offset > QCOW_MAX_SNAPSHOTS_SIZE) {
+ ret = -EFBIG;
+ goto fail;
+ }
}
+ assert(offset - s->snapshots_offset <= INT_MAX);
s->snapshots_size = offset - s->snapshots_offset;
return 0;
@@ -138,7 +144,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
uint32_t nb_snapshots;
uint64_t snapshots_offset;
} QEMU_PACKED header_data;
- int64_t offset, snapshots_offset;
+ int64_t offset, snapshots_offset = 0;
int ret;
/* compute the size of the snapshots */
@@ -150,7 +156,14 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
offset += sizeof(extra);
offset += strlen(sn->id_str);
offset += strlen(sn->name);
+
+ if (offset > QCOW_MAX_SNAPSHOTS_SIZE) {
+ ret = -EFBIG;
+ goto fail;
+ }
}
+
+ assert(offset <= INT_MAX);
snapshots_size = offset;
/* Allocate space for the new snapshot list */
diff --git a/block/qcow2.h b/block/qcow2.h
index 0912488..797c9d8 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -48,6 +48,10 @@
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
#define QCOW_MAX_L1_SIZE 0x2000000
+/* Allow for an average of 1k per snapshot table entry, should be plenty of
+ * space for snapshot names and IDs */
+#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS)
+
/* indicate that the refcount of the referenced cluster is exactly one. */
#define QCOW_OFLAG_COPIED (1LL << 63)
/* indicate that the cluster is compressed (they never have the copied flag) */

View File

@ -0,0 +1,52 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:08 +0100
Subject: [PATCH] parallels: Fix catalog size integer overflow (CVE-2014-0143)
The first test case would cause a huge memory allocation, leading to a
qemu abort; the second one to a too small malloc() for the catalog
(smaller than s->catalog_size), which causes a read-only out-of-bounds
array access and on big endian hosts an endianess conversion for an
undefined memory area.
The sample image used here is not an original Parallels image. It was
created using an hexeditor on the basis of the struct that qemu uses.
Good enough for trying to crash the driver, but not for ensuring
compatibility.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit afbcc40bee4ef51731102d7d4b499ee12fc182e1)
Conflicts:
tests/qemu-iotests/common
tests/qemu-iotests/group
---
block/parallels.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/block/parallels.c b/block/parallels.c
index 18b3ac0..bad47f4 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -49,7 +49,7 @@ typedef struct BDRVParallelsState {
CoMutex lock;
uint32_t *catalog_bitmap;
- int catalog_size;
+ unsigned int catalog_size;
int tracks;
} BDRVParallelsState;
@@ -93,6 +93,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
s->tracks = le32_to_cpu(ph.tracks);
s->catalog_size = le32_to_cpu(ph.catalog_entries);
+ if (s->catalog_size > INT_MAX / 4) {
+ fprintf(stderr, "Catalog too large");
+ ret = -EFBIG;
+ goto fail;
+ }
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4);

View File

@ -0,0 +1,47 @@
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 26 Mar 2014 13:06:09 +0100
Subject: [PATCH] parallels: Sanity check for s->tracks (CVE-2014-0142)
This avoids a possible division by zero.
Convert s->tracks to unsigned as well because it feels better than
surviving just because the results of calculations with s->tracks are
converted to unsigned anyway.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 9302e863aa8baa5d932fc078967050c055fa1a7f)
Conflicts:
tests/qemu-iotests/076
tests/qemu-iotests/076.out
---
block/parallels.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/block/parallels.c b/block/parallels.c
index bad47f4..4bcfe06 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -51,7 +51,7 @@ typedef struct BDRVParallelsState {
uint32_t *catalog_bitmap;
unsigned int catalog_size;
- int tracks;
+ unsigned int tracks;
} BDRVParallelsState;
static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
@@ -91,6 +91,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
bs->total_sectors = le32_to_cpu(ph.nb_sectors);
s->tracks = le32_to_cpu(ph.tracks);
+ if (s->tracks == 0) {
+ fprintf(stderr, "Invalid image: Zero sectors per track");
+ ret = -EINVAL;
+ goto fail;
+ }
s->catalog_size = le32_to_cpu(ph.catalog_entries);
if (s->catalog_size > INT_MAX / 4) {

View File

@ -0,0 +1,50 @@
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Fri, 11 Apr 2014 15:18:08 +0300
Subject: [PATCH] virtio-net: fix guest-triggerable buffer overrun
When VM guest programs multicast addresses for
a virtio net card, it supplies a 32 bit
entries counter for the number of addresses.
These addresses are read into tail portion of
a fixed macs array which has size MAC_TABLE_ENTRIES,
at offset equal to in_use.
To avoid overflow of this array by guest, qemu attempts
to test the size as follows:
- if (in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
however, as mac_data.entries is uint32_t, this sum
can overflow, e.g. if in_use is 1 and mac_data.entries
is 0xffffffff then in_use + mac_data.entries will be 0.
Qemu will then read guest supplied buffer into this
memory, overflowing buffer on heap.
CVE-2014-0150
Cc: qemu-stable@nongnu.org
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-id: 1397218574-25058-1-git-send-email-mst@redhat.com
Reviewed-by: Michael Tokarev <mjt@tls.msk.ru>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit edc243851279e3393000b28b6b69454cae1190ef)
Conflicts:
hw/net/virtio-net.c
---
hw/net/virtio-net.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 060b900..63f777f 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -655,7 +655,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
goto error;
}
- if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
+ if (mac_data.entries <= MAC_TABLE_ENTRIES - n->mac_table.in_use) {
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
mac_data.entries * ETH_ALEN);
if (s != mac_data.entries * ETH_ALEN) {

View File

@ -0,0 +1,95 @@
From: Dmitry Fleytman <dmitry@daynix.com>
Date: Fri, 4 Apr 2014 12:45:19 +0300
Subject: [PATCH] vmxnet3: validate interrupt indices coming from guest
CVE-2013-4544
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
Reported-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-id: 1396604722-11902-2-git-send-email-dmitry@daynix.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 8c6c0478996e8f77374e69b6df68655b0b4ba689)
Conflicts:
hw/net/vmxnet3.c
---
hw/net/vmxnet3.c | 36 ++++++++++++++++++++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 49c2466..6ae39a4 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -52,6 +52,9 @@
#define VMXNET3_DEVICE_VERSION 0x1
#define VMXNET3_DEVICE_REVISION 0x1
+/* Number of interrupt vectors for non-MSIx modes */
+#define VMXNET3_MAX_NMSIX_INTRS (1)
+
/* Macros for rings descriptors access */
#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \
(vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
@@ -1299,6 +1302,34 @@ static void vmxnet3_update_features(VMXNET3State *s)
}
}
+static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx)
+{
+ int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS;
+ if (idx >= max_ints) {
+ hw_error("Bad interrupt index: %d\n", idx);
+ }
+}
+
+static void vmxnet3_validate_interrupts(VMXNET3State *s)
+{
+ int i;
+
+ VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx);
+ vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx);
+
+ for (i = 0; i < s->txq_num; i++) {
+ int idx = s->txq_descr[i].intr_idx;
+ VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx);
+ vmxnet3_validate_interrupt_idx(s->msix_used, idx);
+ }
+
+ for (i = 0; i < s->rxq_num; i++) {
+ int idx = s->rxq_descr[i].intr_idx;
+ VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx);
+ vmxnet3_validate_interrupt_idx(s->msix_used, idx);
+ }
+}
+
static void vmxnet3_activate_device(VMXNET3State *s)
{
int i;
@@ -1438,6 +1469,8 @@ static void vmxnet3_activate_device(VMXNET3State *s)
sizeof(s->rxq_descr[i].rxq_stats));
}
+ vmxnet3_validate_interrupts(s);
+
/* Make sure everything is in place before device activation */
smp_wmb();
@@ -1998,7 +2031,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s)
}
}
-#define VMXNET3_MSI_NUM_VECTORS (1)
#define VMXNET3_MSI_OFFSET (0x50)
#define VMXNET3_USE_64BIT (true)
#define VMXNET3_PER_VECTOR_MASK (false)
@@ -2009,7 +2041,7 @@ vmxnet3_init_msi(VMXNET3State *s)
PCIDevice *d = PCI_DEVICE(s);
int res;
- res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS,
+ res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS,
VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
if (0 > res) {
VMW_WRPRN("Failed to initialize MSI, error %d", res);

View File

@ -0,0 +1,54 @@
From: Dmitry Fleytman <dmitry@daynix.com>
Date: Fri, 4 Apr 2014 12:45:20 +0300
Subject: [PATCH] vmxnet3: validate queues configuration coming from guest
CVE-2013-4544
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
Reported-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-id: 1396604722-11902-3-git-send-email-dmitry@daynix.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 9878d173f574df74bde0ff50b2f81009fbee81bb)
---
hw/net/vmxnet3.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 6ae39a4..7325670 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -1330,6 +1330,23 @@ static void vmxnet3_validate_interrupts(VMXNET3State *s)
}
}
+static void vmxnet3_validate_queues(VMXNET3State *s)
+{
+ /*
+ * txq_num and rxq_num are total number of queues
+ * configured by guest. These numbers must not
+ * exceed corresponding maximal values.
+ */
+
+ if (s->txq_num > VMXNET3_DEVICE_MAX_TX_QUEUES) {
+ hw_error("Bad TX queues number: %d\n", s->txq_num);
+ }
+
+ if (s->rxq_num > VMXNET3_DEVICE_MAX_RX_QUEUES) {
+ hw_error("Bad RX queues number: %d\n", s->rxq_num);
+ }
+}
+
static void vmxnet3_activate_device(VMXNET3State *s)
{
int i;
@@ -1375,7 +1392,7 @@ static void vmxnet3_activate_device(VMXNET3State *s)
VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues);
VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num);
- assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES);
+ vmxnet3_validate_queues(s);
qdescr_table_pa =
VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA);

View File

@ -0,0 +1,30 @@
From: Dmitry Fleytman <dmitry@daynix.com>
Date: Fri, 4 Apr 2014 12:45:21 +0300
Subject: [PATCH] vmxnet3: validate interrupt indices read on migration
CVE-2013-4544
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
Reported-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-id: 1396604722-11902-4-git-send-email-dmitry@daynix.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 3c99afc779c2c78718a565ad8c5e98de7c2c7484)
---
hw/net/vmxnet3.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 7325670..20bd3c6 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2384,6 +2384,8 @@ static int vmxnet3_post_load(void *opaque, int version_id)
}
}
+ vmxnet3_validate_interrupts(s);
+
return 0;
}

View File

@ -0,0 +1,29 @@
From: Dmitry Fleytman <dmitry@daynix.com>
Date: Fri, 4 Apr 2014 12:45:22 +0300
Subject: [PATCH] vmxnet3: validate queues configuration read on migration
CVE-2013-4544
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
Reported-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-id: 1396604722-11902-5-git-send-email-dmitry@daynix.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit f12d048a523780dbda702027d4a91b62af1a08d7)
---
hw/net/vmxnet3.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 20bd3c6..1bbea3b 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2384,6 +2384,7 @@ static int vmxnet3_post_load(void *opaque, int version_id)
}
}
+ vmxnet3_validate_queues(s);
vmxnet3_validate_interrupts(s);
return 0;

View File

@ -0,0 +1,37 @@
From: =?UTF-8?q?Beno=C3=AEt=20Canet?= <benoit.canet@irqsave.net>
Date: Sat, 12 Apr 2014 22:59:50 +0200
Subject: [PATCH] ide: Correct improper smart self test counter reset in ide
core.
The SMART self test counter was incorrectly being reset to zero,
not 1. This had the effect that on every 21st SMART EXECUTE OFFLINE:
* We would write off the beginning of a dynamically allocated buffer
* We forgot the SMART history
Fix this.
Signed-off-by: Benoit Canet <benoit@irqsave.net>
Message-id: 1397336390-24664-1-git-send-email-benoit.canet@irqsave.net
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Cc: qemu-stable@nongnu.org
Acked-by: Kevin Wolf <kwolf@redhat.com>
[PMM: tweaked commit message as per suggestions from Markus]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 940973ae0b45c9b6817bab8e4cf4df99a9ef83d7)
---
hw/ide/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/ide/core.c b/hw/ide/core.c
index a73af72..3fe67ca 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1601,7 +1601,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd)
case 2: /* extended self test */
s->smart_selftest_count++;
if (s->smart_selftest_count > 21) {
- s->smart_selftest_count = 0;
+ s->smart_selftest_count = 1;
}
n = 2 + (s->smart_selftest_count - 1) * 24;
s->smart_selftest_data[n] = s->sector;

View File

@ -0,0 +1,29 @@
From: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Date: Mon, 10 Feb 2014 22:49:35 -0800
Subject: [PATCH] char/serial: Fix emptyness check
This was guarding against a full fifo rather than an empty fifo when
popping. Fix.
Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(cherry picked from commit 88c1ee73d3231c74ff90bcfc084a7589670ec244)
---
hw/char/serial.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 6025592..2989ca2 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -224,7 +224,7 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
if (s->tsr_retry <= 0) {
if (s->fcr & UART_FCR_FE) {
- s->tsr = fifo8_is_full(&s->xmit_fifo) ?
+ s->tsr = fifo8_is_empty(&s->xmit_fifo) ?
0 : fifo8_pop(&s->xmit_fifo);
if (!s->xmit_fifo.num) {
s->lsr |= UART_LSR_THRE;

View File

@ -0,0 +1,37 @@
From: Don Slutz <dslutz@verizon.com>
Date: Tue, 18 Mar 2014 12:29:34 -0400
Subject: [PATCH] char/serial: Fix emptyness handling
The commit 88c1ee73d3231c74ff90bcfc084a7589670ec244
char/serial: Fix emptyness check
Still causes extra NULL byte(s) to be sent.
So if the fifo is empty, do not send an extra NULL byte.
Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Signed-off-by: Don Slutz <dslutz@verizon.com>
Message-id: 1395160174-16006-1-git-send-email-dslutz@verizon.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit dffacd4654ec8bf2898aed230852154c6ed755ed)
---
hw/char/serial.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 2989ca2..6d10747 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -224,8 +224,10 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
if (s->tsr_retry <= 0) {
if (s->fcr & UART_FCR_FE) {
- s->tsr = fifo8_is_empty(&s->xmit_fifo) ?
- 0 : fifo8_pop(&s->xmit_fifo);
+ if (fifo8_is_empty(&s->xmit_fifo)) {
+ return FALSE;
+ }
+ s->tsr = fifo8_pop(&s->xmit_fifo);
if (!s->xmit_fifo.num) {
s->lsr |= UART_LSR_THRE;
}

View File

@ -0,0 +1,34 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Fri, 20 Sep 2013 20:35:06 +0100
Subject: [PATCH] vmstate: Add uint32 2D-array support
Add support for saving VMState of 2D arrays of uint32 values.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit a1b1d277cdaac98f25be249e7819aac781a35530)
---
include/migration/vmstate.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 1c31b5d..e5538c7 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -633,9 +633,15 @@ extern const VMStateInfo vmstate_info_bitmap;
#define VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
+#define VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, _v) \
+ VMSTATE_2DARRAY(_f, _s, _n1, _n2, _v, vmstate_info_uint32, uint32_t)
+
#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
+#define VMSTATE_UINT32_2DARRAY(_f, _s, _n1, _n2) \
+ VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, 0)
+
#define VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint64, uint64_t)

View File

@ -0,0 +1,312 @@
From: =?UTF-8?q?Andreas=20F=C3=A4rber?= <afaerber@suse.de>
Date: Tue, 23 Jul 2013 03:37:49 +0200
Subject: [PATCH] arm_gic: Extract headers hw/intc/arm_gic{,_common}.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Rename NCPU to GIC_NCPU and move GICState away from gic_internal.h.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Andreas Färber <afaerber@suse.de>
(cherry picked from commit 83728796ad3f2ce7d6162c1cb894528b12915646)
---
hw/intc/arm_gic_common.c | 18 ++++----
hw/intc/gic_internal.h | 80 +---------------------------------
include/hw/intc/arm_gic.h | 42 ++++++++++++++++++
include/hw/intc/arm_gic_common.h | 92 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 145 insertions(+), 87 deletions(-)
create mode 100644 include/hw/intc/arm_gic.h
create mode 100644 include/hw/intc/arm_gic_common.h
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 709b5c2..c765850 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -64,17 +64,17 @@ static const VMStateDescription vmstate_gic = {
.post_load = gic_post_load,
.fields = (VMStateField[]) {
VMSTATE_BOOL(enabled, GICState),
- VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, NCPU),
+ VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU),
VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1,
vmstate_gic_irq_state, gic_irq_state),
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
- VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, NCPU),
+ VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
- VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, NCPU),
- VMSTATE_UINT16_ARRAY(priority_mask, GICState, NCPU),
- VMSTATE_UINT16_ARRAY(running_irq, GICState, NCPU),
- VMSTATE_UINT16_ARRAY(running_priority, GICState, NCPU),
- VMSTATE_UINT16_ARRAY(current_pending, GICState, NCPU),
+ VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
+ VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
+ VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
+ VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
+ VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
VMSTATE_END_OF_LIST()
}
};
@@ -84,9 +84,9 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp)
GICState *s = ARM_GIC_COMMON(dev);
int num_irq = s->num_irq;
- if (s->num_cpu > NCPU) {
+ if (s->num_cpu > GIC_NCPU) {
error_setg(errp, "requested %u CPUs exceeds GIC maximum %d",
- s->num_cpu, NCPU);
+ s->num_cpu, GIC_NCPU);
return;
}
s->num_irq += GIC_BASE_IRQ;
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 1426437..3989fd1 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -21,16 +21,9 @@
#ifndef QEMU_ARM_GIC_INTERNAL_H
#define QEMU_ARM_GIC_INTERNAL_H
-#include "hw/sysbus.h"
+#include "hw/intc/arm_gic.h"
-/* Maximum number of possible interrupts, determined by the GIC architecture */
-#define GIC_MAXIRQ 1020
-/* First 32 are private to each CPU (SGIs and PPIs). */
-#define GIC_INTERNAL 32
-/* Maximum number of possible CPU interfaces, determined by GIC architecture */
-#define NCPU 8
-
-#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1)))
+#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1)))
/* The NVIC has 16 internal vectors. However these are not exposed
through the normal GIC interface. */
@@ -59,48 +52,6 @@
s->priority2[(irq) - GIC_INTERNAL])
#define GIC_TARGET(irq) s->irq_target[irq]
-typedef struct gic_irq_state {
- /* The enable bits are only banked for per-cpu interrupts. */
- uint8_t enabled;
- uint8_t pending;
- uint8_t active;
- uint8_t level;
- bool model; /* 0 = N:N, 1 = 1:N */
- bool trigger; /* nonzero = edge triggered. */
-} gic_irq_state;
-
-typedef struct GICState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- qemu_irq parent_irq[NCPU];
- bool enabled;
- bool cpu_enabled[NCPU];
-
- gic_irq_state irq_state[GIC_MAXIRQ];
- uint8_t irq_target[GIC_MAXIRQ];
- uint8_t priority1[GIC_INTERNAL][NCPU];
- uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
- uint16_t last_active[GIC_MAXIRQ][NCPU];
-
- uint16_t priority_mask[NCPU];
- uint16_t running_irq[NCPU];
- uint16_t running_priority[NCPU];
- uint16_t current_pending[NCPU];
-
- uint32_t num_cpu;
-
- MemoryRegion iomem; /* Distributor */
- /* This is just so we can have an opaque pointer which identifies
- * both this GIC and which CPU interface we should be accessing.
- */
- struct GICState *backref[NCPU];
- MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */
- uint32_t num_irq;
- uint32_t revision;
-} GICState;
-
/* The special cases for the revision property: */
#define REV_11MPCORE 0
#define REV_NVIC 0xffffffff
@@ -111,31 +62,4 @@ void gic_complete_irq(GICState *s, int cpu, int irq);
void gic_update(GICState *s);
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
-#define TYPE_ARM_GIC_COMMON "arm_gic_common"
-#define ARM_GIC_COMMON(obj) \
- OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC_COMMON)
-#define ARM_GIC_COMMON_CLASS(klass) \
- OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON)
-#define ARM_GIC_COMMON_GET_CLASS(obj) \
- OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON)
-
-typedef struct ARMGICCommonClass {
- SysBusDeviceClass parent_class;
- void (*pre_save)(GICState *s);
- void (*post_load)(GICState *s);
-} ARMGICCommonClass;
-
-#define TYPE_ARM_GIC "arm_gic"
-#define ARM_GIC(obj) \
- OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC)
-#define ARM_GIC_CLASS(klass) \
- OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC)
-#define ARM_GIC_GET_CLASS(obj) \
- OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC)
-
-typedef struct ARMGICClass {
- ARMGICCommonClass parent_class;
- DeviceRealize parent_realize;
-} ARMGICClass;
-
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h
new file mode 100644
index 0000000..0971e37
--- /dev/null
+++ b/include/hw/intc/arm_gic.h
@@ -0,0 +1,42 @@
+/*
+ * ARM GIC support
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_ARM_GIC_H
+#define HW_ARM_GIC_H
+
+#include "arm_gic_common.h"
+
+#define TYPE_ARM_GIC "arm_gic"
+#define ARM_GIC(obj) \
+ OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC)
+#define ARM_GIC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC)
+#define ARM_GIC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC)
+
+typedef struct ARMGICClass {
+ /*< private >*/
+ ARMGICCommonClass parent_class;
+ /*< public >*/
+
+ DeviceRealize parent_realize;
+} ARMGICClass;
+
+#endif
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
new file mode 100644
index 0000000..4f381bd
--- /dev/null
+++ b/include/hw/intc/arm_gic_common.h
@@ -0,0 +1,92 @@
+/*
+ * ARM GIC support
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_ARM_GIC_COMMON_H
+#define HW_ARM_GIC_COMMON_H
+
+#include "hw/sysbus.h"
+
+/* Maximum number of possible interrupts, determined by the GIC architecture */
+#define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
+/* Maximum number of possible CPU interfaces, determined by GIC architecture */
+#define GIC_NCPU 8
+
+typedef struct gic_irq_state {
+ /* The enable bits are only banked for per-cpu interrupts. */
+ uint8_t enabled;
+ uint8_t pending;
+ uint8_t active;
+ uint8_t level;
+ bool model; /* 0 = N:N, 1 = 1:N */
+ bool trigger; /* nonzero = edge triggered. */
+} gic_irq_state;
+
+typedef struct GICState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ qemu_irq parent_irq[GIC_NCPU];
+ bool enabled;
+ bool cpu_enabled[GIC_NCPU];
+
+ gic_irq_state irq_state[GIC_MAXIRQ];
+ uint8_t irq_target[GIC_MAXIRQ];
+ uint8_t priority1[GIC_INTERNAL][GIC_NCPU];
+ uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
+ uint16_t last_active[GIC_MAXIRQ][GIC_NCPU];
+
+ uint16_t priority_mask[GIC_NCPU];
+ uint16_t running_irq[GIC_NCPU];
+ uint16_t running_priority[GIC_NCPU];
+ uint16_t current_pending[GIC_NCPU];
+
+ uint32_t num_cpu;
+
+ MemoryRegion iomem; /* Distributor */
+ /* This is just so we can have an opaque pointer which identifies
+ * both this GIC and which CPU interface we should be accessing.
+ */
+ struct GICState *backref[GIC_NCPU];
+ MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */
+ uint32_t num_irq;
+ uint32_t revision;
+} GICState;
+
+#define TYPE_ARM_GIC_COMMON "arm_gic_common"
+#define ARM_GIC_COMMON(obj) \
+ OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC_COMMON)
+#define ARM_GIC_COMMON_CLASS(klass) \
+ OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON)
+#define ARM_GIC_COMMON_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON)
+
+typedef struct ARMGICCommonClass {
+ /*< private >*/
+ SysBusDeviceClass parent_class;
+ /*< public >*/
+
+ void (*pre_save)(GICState *s);
+ void (*post_load)(GICState *s);
+} ARMGICCommonClass;
+
+#endif

View File

@ -0,0 +1,124 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Fri, 20 Dec 2013 22:09:32 -0800
Subject: [PATCH] arm_gic: Rename GIC_X_TRIGGER to GIC_X_EDGE_TRIGGER
TRIGGER can really mean mean anything (e.g. was it triggered, is it
level-triggered, is it edge-triggered, etc.). Rename to EDGE_TRIGGER to
make the code comprehensible without looking up the data structure.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Message-id: 1387606179-22709-2-git-send-email-christoffer.dall@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 04050c5c6aa6f9c086a63a30b182b996fb2d3d02)
---
hw/intc/arm_gic.c | 12 ++++++------
hw/intc/arm_gic_common.c | 4 ++--
hw/intc/gic_internal.h | 6 +++---
include/hw/intc/arm_gic_common.h | 2 +-
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index d431b7a..27c258a 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -128,7 +128,7 @@ static void gic_set_irq(void *opaque, int irq, int level)
if (level) {
GIC_SET_LEVEL(irq, cm);
- if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
DPRINTF("Set %d pending mask %x\n", irq, target);
GIC_SET_PENDING(irq, target);
}
@@ -188,7 +188,7 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
return; /* No active IRQ. */
/* Mark level triggered interrupts as pending if they are still
raised. */
- if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
@@ -311,7 +311,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
for (i = 0; i < 4; i++) {
if (GIC_TEST_MODEL(irq + i))
res |= (1 << (i * 2));
- if (GIC_TEST_TRIGGER(irq + i))
+ if (GIC_TEST_EDGE_TRIGGER(irq + i))
res |= (2 << (i * 2));
}
} else if (offset < 0xfe0) {
@@ -386,7 +386,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
/* If a raised level triggered IRQ enabled then mark
is as pending. */
if (GIC_TEST_LEVEL(irq + i, mask)
- && !GIC_TEST_TRIGGER(irq + i)) {
+ && !GIC_TEST_EDGE_TRIGGER(irq + i)) {
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
GIC_SET_PENDING(irq + i, mask);
}
@@ -478,9 +478,9 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
GIC_CLEAR_MODEL(irq + i);
}
if (value & (2 << (i * 2))) {
- GIC_SET_TRIGGER(irq + i);
+ GIC_SET_EDGE_TRIGGER(irq + i);
} else {
- GIC_CLEAR_TRIGGER(irq + i);
+ GIC_CLEAR_EDGE_TRIGGER(irq + i);
}
}
} else {
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index c765850..710607b 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -51,7 +51,7 @@ static const VMStateDescription vmstate_gic_irq_state = {
VMSTATE_UINT8(active, gic_irq_state),
VMSTATE_UINT8(level, gic_irq_state),
VMSTATE_BOOL(model, gic_irq_state),
- VMSTATE_BOOL(trigger, gic_irq_state),
+ VMSTATE_BOOL(edge_trigger, gic_irq_state),
VMSTATE_END_OF_LIST()
}
};
@@ -126,7 +126,7 @@ static void arm_gic_common_reset(DeviceState *dev)
}
for (i = 0; i < 16; i++) {
GIC_SET_ENABLED(i, ALL_CPU_MASK);
- GIC_SET_TRIGGER(i);
+ GIC_SET_EDGE_TRIGGER(i);
}
if (s->num_cpu == 1) {
/* For uniprocessor GICs all interrupts always target the sole CPU */
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 3989fd1..efac78d 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -44,9 +44,9 @@
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
-#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = true
-#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = false
-#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true
+#define GIC_CLEAR_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = false
+#define GIC_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger)
#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \
s->priority1[irq][cpu] : \
s->priority2[(irq) - GIC_INTERNAL])
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 4f381bd..0d232df 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -37,7 +37,7 @@ typedef struct gic_irq_state {
uint8_t active;
uint8_t level;
bool model; /* 0 = N:N, 1 = 1:N */
- bool trigger; /* nonzero = edge triggered. */
+ bool edge_trigger; /* true: edge-triggered, false: level-triggered */
} gic_irq_state;
typedef struct GICState {

View File

@ -0,0 +1,62 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Fri, 20 Dec 2013 22:09:33 -0800
Subject: [PATCH] hw: arm_gic: Introduce gic_set_priority function
To make the code slightly cleaner to look at and make the save/restore
code easier to understand, introduce this function to set the priority of
interrupts.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Message-id: 1387606179-22709-3-git-send-email-christoffer.dall@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 9df90ad078ec782d1339bd6879b6ea117f9759f7)
---
hw/intc/arm_gic.c | 15 ++++++++++-----
hw/intc/gic_internal.h | 1 +
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 27c258a..6c59650 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -168,6 +168,15 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu)
return new_irq;
}
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
+{
+ if (irq < GIC_INTERNAL) {
+ s->priority1[irq][cpu] = val;
+ } else {
+ s->priority2[(irq) - GIC_INTERNAL] = val;
+ }
+}
+
void gic_complete_irq(GICState *s, int cpu, int irq)
{
int update = 0;
@@ -443,11 +452,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < GIC_INTERNAL) {
- s->priority1[irq][cpu] = value;
- } else {
- s->priority2[irq - GIC_INTERNAL] = value;
- }
+ gic_set_priority(s, cpu, irq, value);
} else if (offset < 0xc00) {
/* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the
* annoying exception of the 11MPCore's GIC.
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index efac78d..8c02d58 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -61,5 +61,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu);
void gic_complete_irq(GICState *s, int cpu, int irq);
void gic_update(GICState *s);
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
#endif /* !QEMU_ARM_GIC_INTERNAL_H */

View File

@ -0,0 +1,70 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Fri, 31 Jan 2014 14:47:38 +0000
Subject: [PATCH] arm_gic: Introduce define for GIC_NR_SGIS
Instead of hardcoding 16 various places in the code, use a define to
make it more clear what is going on.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 41ab7b55108e2699e7c2e77788465cb52a0b2c08)
---
hw/intc/arm_gic.c | 17 +++++++++++------
include/hw/intc/arm_gic_common.h | 1 +
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 6c59650..0ce11ac 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -380,8 +380,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < 16)
- value = 0xff;
+ if (irq < GIC_NR_SGIS) {
+ value = 0xff;
+ }
+
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int mask =
@@ -406,8 +408,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < 16)
- value = 0;
+ if (irq < GIC_NR_SGIS) {
+ value = 0;
+ }
+
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
@@ -423,8 +427,9 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < 16)
- irq = 0;
+ if (irq < GIC_NR_SGIS) {
+ irq = 0;
+ }
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 0d232df..8a2aa00 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -27,6 +27,7 @@
#define GIC_MAXIRQ 1020
/* First 32 are private to each CPU (SGIs and PPIs). */
#define GIC_INTERNAL 32
+#define GIC_NR_SGIS 16
/* Maximum number of possible CPU interfaces, determined by GIC architecture */
#define GIC_NCPU 8

View File

@ -0,0 +1,39 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Fri, 31 Jan 2014 14:47:38 +0000
Subject: [PATCH] arm_gic: Fix GICD_ICPENDR and GICD_ISPENDR writes
Fix two bugs that would allow changing the state of SGIs through the
ICPENDR and ISPENDRs.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 5b0adce156216fb24dcc5f1683e8b686f3793fff)
---
hw/intc/arm_gic.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 0ce11ac..62153fd 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -428,7 +428,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
if (irq >= s->num_irq)
goto bad_reg;
if (irq < GIC_NR_SGIS) {
- irq = 0;
+ value = 0;
}
for (i = 0; i < 8; i++) {
@@ -441,6 +441,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
+ if (irq < GIC_NR_SGIS) {
+ value = 0;
+ }
+
for (i = 0; i < 8; i++) {
/* ??? This currently clears the pending bit for all CPUs, even
for per-CPU interrupts. It's unclear whether this is the

View File

@ -0,0 +1,196 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Mon, 18 Nov 2013 20:32:00 -0800
Subject: [PATCH] arm_gic: Fix GIC pending behavior
The existing implementation of the pending behavior in gic_set_irq,
gic_complete_irq, and the distributor pending set/clear registers does
not follow the semantics of the GICv2.0 specs, but may implement the
11MPCore support. Therefore, maintain the existing semantics for
11MPCore and v7M NVIC and change the behavior to be in accordance with
the GICv2.0 specs for "generic implementations" (s->revision == 1 ||
s->revision == 2).
Generic implementations distinguish between setting a level-triggered
interrupt pending through writes to the GICD_ISPENDR and when hardware
raises the interrupt line. Writing to the GICD_ICPENDR will not cause
the interrupt to become non-pending if the line is still active, and
conversely, if the line is deactivated but the interrupt is marked as
pending through a write to GICD_ISPENDR, the interrupt remains pending.
Handle this situation in the GIC_TEST_PENDING (which now becomes a
static inline named gic_test_pending) and let the 'pending' field
correspond only to the latched state of the D-flip flop in the GICv2.0
specs Figure 4-10.
The following changes are added:
gic_test_pending:
Make this a static inline and split out the 11MPCore from the generic
behavior. For the generic behavior, consider interrupts pending if:
((s->irq_state[irq].pending & (cm) != 0) ||
(!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
gic_set_irq:
Split out the 11MPCore from the generic behavior. For the generic
behavior, always GIC_SET_LEVEL() on positive level, but only
GIC_SET_PENDING for edge-triggered interrupts and always simply
GIC_CLEAR_LEVEL() on negative level.
gic_complete_irq:
Only resample the line for line-triggered interrupts on an 11MPCore.
Generic implementations will sample the line directly in
gic_test_pending().
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 8d999995e45c1002aa11f269c98f2e93e6f8c42a)
---
hw/intc/arm_gic.c | 64 ++++++++++++++++++++++++++++++++++++--------------
hw/intc/gic_internal.h | 16 ++++++++++++-
2 files changed, 62 insertions(+), 18 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 62153fd..11fe3c4 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -66,7 +66,7 @@ void gic_update(GICState *s)
best_prio = 0x100;
best_irq = 1023;
for (irq = 0; irq < s->num_irq; irq++) {
- if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
+ if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) {
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
best_prio = GIC_GET_PRIORITY(irq, cpu);
best_irq = irq;
@@ -89,14 +89,43 @@ void gic_set_pending_private(GICState *s, int cpu, int irq)
{
int cm = 1 << cpu;
- if (GIC_TEST_PENDING(irq, cm))
+ if (gic_test_pending(s, irq, cm)) {
return;
+ }
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
GIC_SET_PENDING(irq, cm);
gic_update(s);
}
+static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
+ int cm, int target)
+{
+ if (level) {
+ GIC_SET_LEVEL(irq, cm);
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
+ DPRINTF("Set %d pending mask %x\n", irq, target);
+ GIC_SET_PENDING(irq, target);
+ }
+ } else {
+ GIC_CLEAR_LEVEL(irq, cm);
+ }
+}
+
+static void gic_set_irq_generic(GICState *s, int irq, int level,
+ int cm, int target)
+{
+ if (level) {
+ GIC_SET_LEVEL(irq, cm);
+ DPRINTF("Set %d pending mask %x\n", irq, target);
+ if (GIC_TEST_EDGE_TRIGGER(irq)) {
+ GIC_SET_PENDING(irq, target);
+ }
+ } else {
+ GIC_CLEAR_LEVEL(irq, cm);
+ }
+}
+
/* Process a change in an external IRQ input. */
static void gic_set_irq(void *opaque, int irq, int level)
{
@@ -126,15 +155,12 @@ static void gic_set_irq(void *opaque, int irq, int level)
return;
}
- if (level) {
- GIC_SET_LEVEL(irq, cm);
- if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
- DPRINTF("Set %d pending mask %x\n", irq, target);
- GIC_SET_PENDING(irq, target);
- }
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ gic_set_irq_11mpcore(s, irq, level, cm, target);
} else {
- GIC_CLEAR_LEVEL(irq, cm);
+ gic_set_irq_generic(s, irq, level, cm, target);
}
+
gic_update(s);
}
@@ -195,14 +221,18 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
}
if (s->running_irq[cpu] == 1023)
return; /* No active IRQ. */
- /* Mark level triggered interrupts as pending if they are still
- raised. */
- if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
- && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
- DPRINTF("Set %d pending mask %x\n", irq, cm);
- GIC_SET_PENDING(irq, cm);
- update = 1;
+
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ /* Mark level triggered interrupts as pending if they are still
+ raised. */
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
+ GIC_SET_PENDING(irq, cm);
+ update = 1;
+ }
}
+
if (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
int tmp = s->running_irq[cpu];
@@ -273,7 +303,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
res = 0;
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
- if (GIC_TEST_PENDING(irq + i, mask)) {
+ if (gic_test_pending(s, irq + i, mask)) {
res |= (1 << i);
}
}
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 8c02d58..92a6f7a 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -34,7 +34,6 @@
#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0)
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
-#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
@@ -63,4 +62,19 @@ void gic_update(GICState *s);
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
+static inline bool gic_test_pending(GICState *s, int irq, int cm)
+{
+ if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
+ return s->irq_state[irq].pending & cm;
+ } else {
+ /* Edge-triggered interrupts are marked pending on a rising edge, but
+ * level-triggered interrupts are either considered pending when the
+ * level is active or if software has explicitly written to
+ * GICD_ISPENDR to set the state pending.
+ */
+ return (s->irq_state[irq].pending & cm) ||
+ (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm));
+ }
+}
+
#endif /* !QEMU_ARM_GIC_INTERNAL_H */

View File

@ -0,0 +1,217 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Mon, 18 Nov 2013 20:32:00 -0800
Subject: [PATCH] arm_gic: Keep track of SGI sources
Right now the arm gic emulation doesn't keep track of the source of an
SGI (which apparently Linux guests don't use, or they're fine with
assuming CPU 0 always).
Add the necessary matrix on the GICState structure and maintain the data
when setting and clearing the pending state of an IRQ and make the state
visible to the guest.
Note that we always choose to present the source as the lowest-numbered
CPU in case multiple cores have signalled the same SGI number to a core
on the system.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 40d225009efe17cad647b4b7424b77a3ace232f1)
---
hw/intc/arm_gic.c | 98 +++++++++++++++++++++++++++++++++++-----
hw/intc/arm_gic_common.c | 5 +-
include/hw/intc/arm_gic_common.h | 7 +++
3 files changed, 96 insertions(+), 14 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 11fe3c4..29f98be 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -151,6 +151,8 @@ static void gic_set_irq(void *opaque, int irq, int level)
target = cm;
}
+ assert(irq >= GIC_NR_SGIS);
+
if (level == GIC_TEST_LEVEL(irq, cm)) {
return;
}
@@ -177,21 +179,48 @@ static void gic_set_running_irq(GICState *s, int cpu, int irq)
uint32_t gic_acknowledge_irq(GICState *s, int cpu)
{
- int new_irq;
+ int ret, irq, src;
int cm = 1 << cpu;
- new_irq = s->current_pending[cpu];
- if (new_irq == 1023
- || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
+ irq = s->current_pending[cpu];
+ if (irq == 1023
+ || GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) {
DPRINTF("ACK no pending IRQ\n");
return 1023;
}
- s->last_active[new_irq][cpu] = s->running_irq[cpu];
- /* Clear pending flags for both level and edge triggered interrupts.
- Level triggered IRQs will be reasserted once they become inactive. */
- GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
- gic_set_running_irq(s, cpu, new_irq);
- DPRINTF("ACK %d\n", new_irq);
- return new_irq;
+ s->last_active[irq][cpu] = s->running_irq[cpu];
+
+ if (s->revision == REV_11MPCORE) {
+ /* Clear pending flags for both level and edge triggered interrupts.
+ * Level triggered IRQs will be reasserted once they become inactive.
+ */
+ GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
+ ret = irq;
+ } else {
+ if (irq < GIC_NR_SGIS) {
+ /* Lookup the source CPU for the SGI and clear this in the
+ * sgi_pending map. Return the src and clear the overall pending
+ * state on this CPU if the SGI is not pending from any CPUs.
+ */
+ assert(s->sgi_pending[irq][cpu] != 0);
+ src = ctz32(s->sgi_pending[irq][cpu]);
+ s->sgi_pending[irq][cpu] &= ~(1 << src);
+ if (s->sgi_pending[irq][cpu] == 0) {
+ GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
+ }
+ ret = irq | ((src & 0x7) << 10);
+ } else {
+ /* Clear pending state for both level and edge triggered
+ * interrupts. (level triggered interrupts with an active line
+ * remain pending, see gic_test_pending)
+ */
+ GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
+ ret = irq;
+ }
+ }
+
+ gic_set_running_irq(s, cpu, irq);
+ DPRINTF("ACK %d\n", irq);
+ return ret;
}
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
@@ -353,6 +382,22 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
if (GIC_TEST_EDGE_TRIGGER(irq + i))
res |= (2 << (i * 2));
}
+ } else if (offset < 0xf10) {
+ goto bad_reg;
+ } else if (offset < 0xf30) {
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ goto bad_reg;
+ }
+
+ if (offset < 0xf20) {
+ /* GICD_CPENDSGIRn */
+ irq = (offset - 0xf10);
+ } else {
+ irq = (offset - 0xf20);
+ /* GICD_SPENDSGIRn */
+ }
+
+ res = s->sgi_pending[irq][cpu];
} else if (offset < 0xfe0) {
goto bad_reg;
} else /* offset >= 0xfe0 */ {
@@ -527,9 +572,31 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
GIC_CLEAR_EDGE_TRIGGER(irq + i);
}
}
- } else {
+ } else if (offset < 0xf10) {
/* 0xf00 is only handled for 32-bit writes. */
goto bad_reg;
+ } else if (offset < 0xf20) {
+ /* GICD_CPENDSGIRn */
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ goto bad_reg;
+ }
+ irq = (offset - 0xf10);
+
+ s->sgi_pending[irq][cpu] &= ~value;
+ if (s->sgi_pending[irq][cpu] == 0) {
+ GIC_CLEAR_PENDING(irq, 1 << cpu);
+ }
+ } else if (offset < 0xf30) {
+ /* GICD_SPENDSGIRn */
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ goto bad_reg;
+ }
+ irq = (offset - 0xf20);
+
+ GIC_SET_PENDING(irq, 1 << cpu);
+ s->sgi_pending[irq][cpu] |= value;
+ } else {
+ goto bad_reg;
}
gic_update(s);
return;
@@ -553,6 +620,7 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
int cpu;
int irq;
int mask;
+ int target_cpu;
cpu = gic_get_current_cpu(s);
irq = value & 0x3ff;
@@ -572,6 +640,12 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
break;
}
GIC_SET_PENDING(irq, mask);
+ target_cpu = ctz32(mask);
+ while (target_cpu < GIC_NCPU) {
+ s->sgi_pending[irq][target_cpu] |= (1 << cpu);
+ mask &= ~(1 << target_cpu);
+ target_cpu = ctz32(mask);
+ }
gic_update(s);
return;
}
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 710607b..f4c7f14 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 4,
- .minimum_version_id = 4,
+ .version_id = 5,
+ .minimum_version_id = 5,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
@@ -71,6 +71,7 @@ static const VMStateDescription vmstate_gic = {
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
+ VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU),
VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 8a2aa00..d2e0c2f 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -55,6 +55,13 @@ typedef struct GICState {
uint8_t priority1[GIC_INTERNAL][GIC_NCPU];
uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
uint16_t last_active[GIC_MAXIRQ][GIC_NCPU];
+ /* For each SGI on the target CPU, we store 8 bits
+ * indicating which source CPUs have made this SGI
+ * pending on the target CPU. These correspond to
+ * the bytes in the GIC_SPENDSGIR* registers as
+ * read by the target CPU.
+ */
+ uint8_t sgi_pending[GIC_NR_SGIS][GIC_NCPU];
uint16_t priority_mask[GIC_NCPU];
uint16_t running_irq[GIC_NCPU];

View File

@ -0,0 +1,99 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Thu, 12 Sep 2013 22:18:20 -0700
Subject: [PATCH] arm_gic: Support setting/getting binary point reg
Add a binary_point field to the gic emulation structure and support
setting/getting this register now when we have it. We don't actually
support interrupt grouping yet, oh well.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit aa7d461ae9dd79d35999f4710743cdf9dec88cef)
---
hw/intc/arm_gic.c | 12 +++++++++---
hw/intc/arm_gic_common.c | 6 ++++--
include/hw/intc/arm_gic_common.h | 7 +++++++
3 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 29f98be..d31892d 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -669,14 +669,15 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
case 0x04: /* Priority mask */
return s->priority_mask[cpu];
case 0x08: /* Binary Point */
- /* ??? Not implemented. */
- return 0;
+ return s->bpr[cpu];
case 0x0c: /* Acknowledge */
return gic_acknowledge_irq(s, cpu);
case 0x14: /* Running Priority */
return s->running_priority[cpu];
case 0x18: /* Highest Pending Interrupt */
return s->current_pending[cpu];
+ case 0x1c: /* Aliased Binary Point */
+ return s->abpr[cpu];
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
@@ -695,10 +696,15 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
s->priority_mask[cpu] = (value & 0xff);
break;
case 0x08: /* Binary Point */
- /* ??? Not implemented. */
+ s->bpr[cpu] = (value & 0x7);
break;
case 0x10: /* End Of Interrupt */
return gic_complete_irq(s, cpu, value & 0x3ff);
+ case 0x1c: /* Aliased Binary Point */
+ if (s->revision >= 2) {
+ s->abpr[cpu] = (value & 0x7);
+ }
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_write: Bad offset %x\n", (int)offset);
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index f4c7f14..7966985 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 5,
- .minimum_version_id = 5,
+ .version_id = 6,
+ .minimum_version_id = 6,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
@@ -76,6 +76,8 @@ static const VMStateDescription vmstate_gic = {
VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
+ VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
+ VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
VMSTATE_END_OF_LIST()
}
};
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index d2e0c2f..983c3cf 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -68,6 +68,13 @@ typedef struct GICState {
uint16_t running_priority[GIC_NCPU];
uint16_t current_pending[GIC_NCPU];
+ /* We present the GICv2 without security extensions to a guest and
+ * therefore the guest can configure the GICC_CTLR to configure group 1
+ * binary point in the abpr.
+ */
+ uint8_t bpr[GIC_NCPU];
+ uint8_t abpr[GIC_NCPU];
+
uint32_t num_cpu;
MemoryRegion iomem; /* Distributor */

View File

@ -0,0 +1,104 @@
From: Christoffer Dall <christoffer.dall@linaro.org>
Date: Mon, 18 Nov 2013 19:26:33 -0800
Subject: [PATCH] arm_gic: Add GICC_APRn state to the GICState
The GICC_APRn registers are not currently supported by the ARM GIC v2.0
emulation. This patch adds the missing state.
Note that we also change the number of APRs to use a define GIC_NR_APRS
based on the maximum number of preemption levels. This patch also adds
RAZ/WI accessors for the four registers on the emulated CPU interface.
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit a9d477c4e3d614409a48d12f34624c2dd9f1ec2d)
---
hw/intc/arm_gic.c | 5 +++++
hw/intc/arm_gic_common.c | 5 +++--
include/hw/intc/arm_gic_common.h | 19 +++++++++++++++++++
3 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index d31892d..9ca3f03 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -678,6 +678,8 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
return s->current_pending[cpu];
case 0x1c: /* Aliased Binary Point */
return s->abpr[cpu];
+ case 0xd0: case 0xd4: case 0xd8: case 0xdc:
+ return s->apr[(offset - 0xd0) / 4][cpu];
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
@@ -705,6 +707,9 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
s->abpr[cpu] = (value & 0x7);
}
break;
+ case 0xd0: case 0xd4: case 0xd8: case 0xdc:
+ qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_write: Bad offset %x\n", (int)offset);
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 7966985..ec6286b 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 6,
- .minimum_version_id = 6,
+ .version_id = 7,
+ .minimum_version_id = 7,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
@@ -78,6 +78,7 @@ static const VMStateDescription vmstate_gic = {
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
+ VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
VMSTATE_END_OF_LIST()
}
};
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 983c3cf..89384c2 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -31,6 +31,9 @@
/* Maximum number of possible CPU interfaces, determined by GIC architecture */
#define GIC_NCPU 8
+#define MAX_NR_GROUP_PRIO 128
+#define GIC_NR_APRS (MAX_NR_GROUP_PRIO / 32)
+
typedef struct gic_irq_state {
/* The enable bits are only banked for per-cpu interrupts. */
uint8_t enabled;
@@ -75,6 +78,22 @@ typedef struct GICState {
uint8_t bpr[GIC_NCPU];
uint8_t abpr[GIC_NCPU];
+ /* The APR is implementation defined, so we choose a layout identical to
+ * the KVM ABI layout for QEMU's implementation of the gic:
+ * If an interrupt for preemption level X is active, then
+ * APRn[X mod 32] == 0b1, where n = X / 32
+ * otherwise the bit is clear.
+ *
+ * TODO: rewrite the interrupt acknowlege/complete routines to use
+ * the APR registers to track the necessary information to update
+ * s->running_priority[] on interrupt completion (ie completely remove
+ * last_active[][] and running_irq[]). This will be necessary if we ever
+ * want to support TCG<->KVM migration, or TCG guests which can
+ * do power management involving powering down and restarting
+ * the GIC.
+ */
+ uint32_t apr[GIC_NR_APRS][GIC_NCPU];
+
uint32_t num_cpu;
MemoryRegion iomem; /* Distributor */

View File

@ -0,0 +1,31 @@
From: Peter Maydell <peter.maydell@linaro.org>
Date: Thu, 20 Feb 2014 10:35:48 +0000
Subject: [PATCH] hw/intc/arm_gic: Fix NVIC assertion failure
Commit 40d225009ef accidentally changed the behaviour of
gic_acknowledge_irq() for the NVIC. The NVIC doesn't have SGIs,
so this meant we hit an assertion:
gic_acknowledge_irq: Assertion `s->sgi_pending[irq][cpu] != 0' failed.
Return NVIC acknowledge-irq to its previous behaviour, like 11MPCore.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
(cherry picked from commit 873169022aa58daabd10979002f8009c7e5f3f05)
---
hw/intc/arm_gic.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 9ca3f03..4e0628c 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -189,7 +189,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu)
}
s->last_active[irq][cpu] = s->running_irq[cpu];
- if (s->revision == REV_11MPCORE) {
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Clear pending flags for both level and edge triggered interrupts.
* Level triggered IRQs will be reasserted once they become inactive.
*/

View File

@ -0,0 +1,56 @@
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Thu, 3 Apr 2014 19:50:31 +0300
Subject: [PATCH] vmstate: add VMS_MUST_EXIST
Can be used to verify a required field exists or validate
state in some other way.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
(cherry picked from commit 5bf81c8d63db0216a4d29dc87f9ce530bb791dd1)
---
include/migration/vmstate.h | 1 +
savevm.c | 10 ++++++++++
2 files changed, 11 insertions(+)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index e5538c7..c1717c2 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -100,6 +100,7 @@ enum VMStateFlags {
VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */
VMS_VARRAY_UINT8 = 0x400, /* Array with size in uint8_t field*/
VMS_VARRAY_UINT32 = 0x800, /* Array with size in uint32_t field*/
+ VMS_MUST_EXIST = 0x1000, /* Field must exist in input */
};
typedef struct {
diff --git a/savevm.c b/savevm.c
index 03fc4d9..9f7f668 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1731,6 +1731,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
return ret;
}
}
+ } else if (field->flags & VMS_MUST_EXIST) {
+ fprintf(stderr, "Input validation failed: %s/%s\n",
+ vmsd->name, field->name);
+ return -1;
}
field++;
}
@@ -1791,6 +1795,12 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
field->info->put(f, addr, size);
}
}
+ } else {
+ if (field->flags & VMS_MUST_EXIST) {
+ fprintf(stderr, "Output state validation failed: %s/%s\n",
+ vmsd->name, field->name);
+ assert(!(field->flags & VMS_MUST_EXIST));
+ }
}
field++;
}

View File

@ -0,0 +1,32 @@
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Thu, 3 Apr 2014 19:50:35 +0300
Subject: [PATCH] vmstate: add VMSTATE_VALIDATE
Validate state using VMS_ARRAY with num = 0 and VMS_MUST_EXIST
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
(cherry picked from commit 4082f0889ba04678fc14816c53e1b9251ea9207e)
---
include/migration/vmstate.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index c1717c2..d5a63ce 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -204,6 +204,14 @@ extern const VMStateInfo vmstate_info_bitmap;
.offset = vmstate_offset_value(_state, _field, _type), \
}
+/* Validate state using a boolean predicate. */
+#define VMSTATE_VALIDATE(_name, _test) { \
+ .name = (_name), \
+ .field_exists = (_test), \
+ .flags = VMS_ARRAY | VMS_MUST_EXIST, \
+ .num = 0, /* 0 elements: no data, only run _test */ \
+}
+
#define VMSTATE_POINTER(_field, _state, _version, _info, _type) { \
.name = (stringify(_field)), \
.version_id = (_version), \

View File

@ -0,0 +1,58 @@
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Thu, 3 Apr 2014 19:50:39 +0300
Subject: [PATCH] virtio-net: fix buffer overflow on invalid state load
CVE-2013-4148 QEMU 1.0 integer conversion in
virtio_net_load()@hw/net/virtio-net.c
Deals with loading a corrupted savevm image.
> n->mac_table.in_use = qemu_get_be32(f);
in_use is int so it can get negative when assigned 32bit unsigned value.
> /* MAC_TABLE_ENTRIES may be different from the saved image */
> if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) {
passing this check ^^^
> qemu_get_buffer(f, n->mac_table.macs,
> n->mac_table.in_use * ETH_ALEN);
with good in_use value, "n->mac_table.in_use * ETH_ALEN" can get
positive and bigger than mac_table.macs. For example 0x81000000
satisfies this condition when ETH_ALEN is 6.
Fix it by making the value unsigned.
For consistency, change first_multi as well.
Note: all call sites were audited to confirm that
making them unsigned didn't cause any issues:
it turns out we actually never do math on them,
so it's easy to validate because both values are
always <= MAC_TABLE_ENTRIES.
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
(cherry picked from commit 71f7fe48e10a8437c9d42d859389f37157f59980)
---
include/hw/virtio/virtio-net.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index df60f16..4b32440 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -176,8 +176,8 @@ typedef struct VirtIONet {
uint8_t nobcast;
uint8_t vhost_started;
struct {
- int in_use;
- int first_multi;
+ uint32_t in_use;
+ uint32_t first_multi;
uint8_t multi_overflow;
uint8_t uni_overflow;
uint8_t *macs;

View File

@ -0,0 +1,54 @@
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Thu, 3 Apr 2014 19:50:56 +0300
Subject: [PATCH] virtio-net: out-of-bounds buffer write on invalid state load
CVE-2013-4150 QEMU 1.5.0 out-of-bounds buffer write in
virtio_net_load()@hw/net/virtio-net.c
This code is in hw/net/virtio-net.c:
if (n->max_queues > 1) {
if (n->max_queues != qemu_get_be16(f)) {
error_report("virtio-net: different max_queues ");
return -1;
}
n->curr_queues = qemu_get_be16(f);
for (i = 1; i < n->curr_queues; i++) {
n->vqs[i].tx_waiting = qemu_get_be32(f);
}
}
Number of vqs is max_queues, so if we get invalid input here,
for example if max_queues = 2, curr_queues = 3, we get
write beyond end of the buffer, with data that comes from
wire.
This might be used to corrupt qemu memory in hard to predict ways.
Since we have lots of function pointers around, RCE might be possible.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
(cherry picked from commit eea750a5623ddac7a61982eec8f1c93481857578)
---
hw/net/virtio-net.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 63f777f..5907e96 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1379,6 +1379,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
n->curr_queues = qemu_get_be16(f);
+ if (n->curr_queues > n->max_queues) {
+ error_report("virtio-net: curr_queues %x > max_queues %x",
+ n->curr_queues, n->max_queues);
+ return -1;
+ }
for (i = 1; i < n->curr_queues; i++) {
n->vqs[i].tx_waiting = qemu_get_be32(f);
}

Some files were not shown because too many files have changed in this diff Show More