SECURITY: CVE-2015-5278, CVE-2015-5279, CVE-2015-6815

Some more fixes, mostly security related.

from Brad (maintainer)
This commit is contained in:
ajacoutot 2015-09-16 09:18:13 +00:00
parent 748fa28250
commit 15b06e762f
7 changed files with 295 additions and 2 deletions

View File

@ -1,11 +1,11 @@
# $OpenBSD: Makefile,v 1.131 2015/09/16 07:08:41 ajacoutot Exp $
# $OpenBSD: Makefile,v 1.132 2015/09/16 09:18:13 ajacoutot Exp $
ONLY_FOR_ARCHS= amd64 i386 powerpc sparc64
COMMENT= multi system emulator
DISTNAME= qemu-2.2.1
REVISION= 7
REVISION= 8
CATEGORIES= emulators
MASTER_SITES= http://wiki.qemu.org/download/
EXTRACT_SUFX= .tar.bz2

View File

@ -0,0 +1,35 @@
$OpenBSD: patch-block_vmdk_c,v 1.1 2015/09/16 09:18:13 ajacoutot Exp $
vmdk: Fix overflow if l1_size is 0x20000000
Richard Jones caught this bug with afl fuzzer.
In fact, that's the only possible value to overflow (extent->l1_size =
0x20000000) l1_size:
l1_size = extent->l1_size * sizeof(long) => 0x80000000;
g_try_malloc returns NULL because l1_size is interpreted as negative
during type casting from 'int' to 'gsize', which yields a enormous
value. Hence, by coincidence, we get a "not too bad" behavior:
qemu-img: Could not open '/tmp/afl6.img': Could not open
'/tmp/afl6.img': Cannot allocate memory
Values larger than 0x20000000 will be refused by the validation in
vmdk_add_extent.
Values smaller than 0x20000000 will not overflow l1_size.
--- block/vmdk.c.orig Wed Sep 16 03:46:16 2015
+++ block/vmdk.c Wed Sep 16 03:46:53 2015
@@ -450,7 +450,8 @@ static int vmdk_init_tables(BlockDriverState *bs, Vmdk
Error **errp)
{
int ret;
- int l1_size, i;
+ size_t l1_size;
+ int i;
/* read the L1 table */
l1_size = extent->l1_size * sizeof(uint32_t);

View File

@ -0,0 +1,69 @@
$OpenBSD: patch-block_vpc_c,v 1.1 2015/09/16 09:18:13 ajacoutot Exp $
block: vpc - prevent overflow if max_table_entries >= 0x40000000
When we allocate the pagetable based on max_table_entries, we multiply
the max table entry value by 4 to accomodate a table of 32-bit integers.
However, max_table_entries is a uint32_t, and the VPC driver accepts
ranges for that entry over 0x40000000. So during this allocation:
s->pagetable = qemu_try_blockalign(bs->file, s->max_table_entries * 4);
The size arg overflows, allocating significantly less memory than
expected.
Since qemu_try_blockalign() size argument is size_t, cast the
multiplication correctly to prevent overflow.
The value of "max_table_entries * 4" is used elsewhere in the code as
well, so store the correct value for use in all those cases.
We also check the Max Tables Entries value, to make sure that it is <
SIZE_MAX / 4, so we know the pagetable size will fit in size_t.
--- block/vpc.c.orig Wed Sep 16 03:08:08 2015
+++ block/vpc.c Wed Sep 16 03:11:21 2015
@@ -167,6 +167,7 @@ static int vpc_open(BlockDriverState *bs, QDict *optio
uint8_t buf[HEADER_SIZE];
uint32_t checksum;
uint64_t computed_size;
+ uint64_t pagetable_size;
int disk_type = VHD_DYNAMIC;
int ret;
@@ -269,7 +270,17 @@ static int vpc_open(BlockDriverState *bs, QDict *optio
goto fail;
}
- s->pagetable = qemu_try_blockalign(bs->file, s->max_table_entries * 4);
+ if (s->max_table_entries > SIZE_MAX / 4 ||
+ s->max_table_entries > (int) INT_MAX / 4) {
+ error_setg(errp, "Max Table Entries too large (%" PRId32 ")",
+ s->max_table_entries);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ pagetable_size = (uint64_t) s->max_table_entries * 4;
+
+ s->pagetable = qemu_try_blockalign(bs->file, pagetable_size);
if (s->pagetable == NULL) {
ret = -ENOMEM;
goto fail;
@@ -277,14 +288,13 @@ static int vpc_open(BlockDriverState *bs, QDict *optio
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
- ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable,
- s->max_table_entries * 4);
+ ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size);
if (ret < 0) {
goto fail;
}
s->free_data_block_offset =
- (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
+ ROUND_UP(s->bat_offset + pagetable_size, 512);
for (i = 0; i < s->max_table_entries; i++) {
be32_to_cpus(&s->pagetable[i]);

View File

@ -0,0 +1,22 @@
$OpenBSD: patch-hw_net_e1000_c,v 1.1 2015/09/16 09:18:13 ajacoutot Exp $
e1000: Avoid infinite loop in processing transmit descriptor (CVE-2015-6815)
While processing transmit descriptors, it could lead to an infinite
loop if 'bytes' was to become zero; Add a check to avoid it.
[The guest can force 'bytes' to 0 by setting the hdr_len and mss
descriptor fields to 0.
--- hw/net/e1000.c.orig Wed Sep 16 03:27:05 2015
+++ hw/net/e1000.c Wed Sep 16 03:28:19 2015
@@ -736,7 +736,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *d
memmove(tp->data, tp->header, tp->hdr_len);
tp->size = tp->hdr_len;
}
- } while (split_size -= bytes);
+ split_size -= bytes;
+ } while (bytes && split_size);
} else if (!tp->tse && tp->cptse) {
// context descriptor TSE is not set, while data descriptor TSE is set
DBGOUT(TXERR, "TCP segmentation error\n");

View File

@ -0,0 +1,73 @@
$OpenBSD: patch-hw_net_ne2000_c,v 1.1 2015/09/16 09:18:13 ajacoutot Exp $
net: add checks to validate ring buffer pointers(CVE-2015-5279)
Ne2000 NIC uses ring buffer of NE2000_MEM_SIZE(49152)
bytes to process network packets. While receiving packets
via ne2000_receive() routine, a local 'index' variable
could exceed the ring buffer size, which could lead to a
memory buffer overflow. Added other checks at initialisation.
net: avoid infinite loop when receiving packets(CVE-2015-5278)
Ne2000 NIC uses ring buffer of NE2000_MEM_SIZE(49152)
bytes to process network packets. While receiving packets
via ne2000_receive() routine, a local 'index' variable
could exceed the ring buffer size, leading to an infinite
loop situation.
--- hw/net/ne2000.c.orig Wed Sep 16 03:30:14 2015
+++ hw/net/ne2000.c Wed Sep 16 03:35:01 2015
@@ -230,6 +230,9 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8
}
index = s->curpag << 8;
+ if (index >= NE2000_PMEM_END) {
+ index = s->start;
+ }
/* 4 bytes for header */
total_len = size + 4;
/* address for next packet (4 bytes for CRC) */
@@ -253,7 +256,7 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8
if (index <= s->stop)
avail = s->stop - index;
else
- avail = 0;
+ break;
len = size;
if (len > avail)
len = avail;
@@ -315,13 +318,19 @@ static void ne2000_ioport_write(void *opaque, uint32_t
offset = addr | (page << 4);
switch(offset) {
case EN0_STARTPG:
- s->start = val << 8;
+ if (val << 8 <= NE2000_PMEM_END) {
+ s->start = val << 8;
+ }
break;
case EN0_STOPPG:
- s->stop = val << 8;
+ if (val << 8 <= NE2000_PMEM_END) {
+ s->stop = val << 8;
+ }
break;
case EN0_BOUNDARY:
- s->boundary = val;
+ if (val << 8 < NE2000_PMEM_END) {
+ s->boundary = val;
+ }
break;
case EN0_IMR:
s->imr = val;
@@ -362,7 +371,9 @@ static void ne2000_ioport_write(void *opaque, uint32_t
s->phys[offset - EN1_PHYS] = val;
break;
case EN1_CURPAG:
- s->curpag = val;
+ if (val << 8 < NE2000_PMEM_END) {
+ s->curpag = val;
+ }
break;
case EN1_MULT ... EN1_MULT + 7:
s->mult[offset - EN1_MULT] = val;

View File

@ -0,0 +1,63 @@
$OpenBSD: patch-hw_net_virtio-net_c,v 1.1 2015/09/16 09:18:13 ajacoutot Exp $
virtio-net: unbreak any layout
Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
("virtio-net: byteswap virtio-net header") breaks any layout by
requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
copying header to temporary buffer if swap is needed, and then use
this buffer as part of out_sg.
Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
("virtio-net: byteswap virtio-net header")
--- hw/net/virtio-net.c.orig Wed Sep 16 03:14:46 2015
+++ hw/net/virtio-net.c Wed Sep 16 03:17:00 2015
@@ -1135,7 +1135,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
ssize_t ret, len;
unsigned int out_num = elem.out_num;
struct iovec *out_sg = &elem.out_sg[0];
- struct iovec sg[VIRTQUEUE_MAX_SIZE];
+ struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1];
+ struct virtio_net_hdr_mrg_rxbuf mhdr;
if (out_num < 1) {
error_report("virtio-net header not in first element");
@@ -1143,13 +1144,25 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
}
if (n->has_vnet_hdr) {
- if (out_sg[0].iov_len < n->guest_hdr_len) {
+ if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
+ n->guest_hdr_len) {
error_report("virtio-net header incorrect");
exit(1);
}
- virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base);
+ if (virtio_needs_swap(vdev)) {
+ virtio_net_hdr_swap(vdev, (void *) &mhdr);
+ sg2[0].iov_base = &mhdr;
+ sg2[0].iov_len = n->guest_hdr_len;
+ out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
+ out_sg, out_num,
+ n->guest_hdr_len, -1);
+ if (out_num == VIRTQUEUE_MAX_SIZE) {
+ goto drop;
+ }
+ out_num += 1;
+ out_sg = sg2;
+ }
}
-
/*
* If host wants to see the guest header as is, we can
* pass it on unchanged. Otherwise, copy just the parts
@@ -1179,7 +1192,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
}
len += ret;
-
+drop:
virtqueue_push(q->tx_vq, &elem, 0);
virtio_notify(vdev, q->tx_vq);

View File

@ -0,0 +1,31 @@
$OpenBSD: patch-include_hw_virtio_virtio-access_h,v 1.1 2015/09/16 09:18:13 ajacoutot Exp $
virtio-net: unbreak any layout
Commit 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
("virtio-net: byteswap virtio-net header") breaks any layout by
requiring out_sg[0].iov_len >= n->guest_hdr_len. Fixing this by
copying header to temporary buffer if swap is needed, and then use
this buffer as part of out_sg.
Fixes 032a74a1c0fcdd5fd1c69e56126b4c857ee36611
("virtio-net: byteswap virtio-net header")
--- include/hw/virtio/virtio-access.h.orig Wed Sep 16 03:17:28 2015
+++ include/hw/virtio/virtio-access.h Wed Sep 16 03:18:03 2015
@@ -126,6 +126,15 @@ static inline uint64_t virtio_ldq_p(VirtIODevice *vdev
}
}
+static inline bool virtio_needs_swap(VirtIODevice *vdev)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+ return virtio_access_is_big_endian(vdev) ? false : true;
+#else
+ return virtio_access_is_big_endian(vdev) ? true : false;
+#endif
+}
+
static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s)
{
#ifdef HOST_WORDS_BIGENDIAN