summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2015-07-26 23:42:53 -0400
committerMichael Roth <mdroth@linux.vnet.ibm.com>2015-07-29 22:19:29 -0500
commit9634e45e0b2bd4906778efdfb4b38b6fa042ebec (patch)
tree67c2c512144b2933b600d8cbc321584b18d0f837
parent0dc545e97799d07267854e60ab57b48939edf0e8 (diff)
downloadqemu-9634e45e0b2bd4906778efdfb4b38b6fa042ebec.tar.gz
ide: Check array bounds before writing to io_buffer (CVE-2015-5154)
If the end_transfer_func of a command is called because enough data has been read or written for the current PIO transfer, and it fails to correctly call the command completion functions, the DRQ bit in the status register and s->end_transfer_func may remain set. This allows the guest to access further bytes in s->io_buffer beyond s->data_end, and eventually overflowing the io_buffer. One case where this currently happens is emulation of the ATAPI command START STOP UNIT. This patch fixes the problem by adding explicit array bounds checks before accessing the buffer instead of relying on end_transfer_func to function correctly. Cc: qemu-stable@nongnu.org Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> (cherry picked from commit d2ff85854512574e7209f295e87b0835d5b032c6) Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
-rw-r--r--hw/ide/core.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/hw/ide/core.c b/hw/ide/core.c
index a895fd86f6..17153f5e89 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2021,6 +2021,10 @@ void ide_data_writew(void *opaque, uint32_t addr, uint32_t val)
}
p = s->data_ptr;
+ if (p + 2 > s->data_end) {
+ return;
+ }
+
*(uint16_t *)p = le16_to_cpu(val);
p += 2;
s->data_ptr = p;
@@ -2042,6 +2046,10 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr)
}
p = s->data_ptr;
+ if (p + 2 > s->data_end) {
+ return 0;
+ }
+
ret = cpu_to_le16(*(uint16_t *)p);
p += 2;
s->data_ptr = p;
@@ -2063,6 +2071,10 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val)
}
p = s->data_ptr;
+ if (p + 4 > s->data_end) {
+ return;
+ }
+
*(uint32_t *)p = le32_to_cpu(val);
p += 4;
s->data_ptr = p;
@@ -2084,6 +2096,10 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr)
}
p = s->data_ptr;
+ if (p + 4 > s->data_end) {
+ return 0;
+ }
+
ret = cpu_to_le32(*(uint32_t *)p);
p += 4;
s->data_ptr = p;